Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C++: Remove FPs from cpp/uninitialized-local when encountered extraction errors #17481

Merged
merged 8 commits into from
Oct 2, 2024

Conversation

calumgrant
Copy link
Contributor

@calumgrant calumgrant commented Sep 16, 2024

Fixes false-positives in standalone extraction mode, whereby local variables may be initialized using function calls to unrecognised functions. These manifest as ErrorExpr, so we can exclude these from the results.

The DCA results are interesting. We lose one FP on abseil (this is good), but also a bunch of results on the samate project, which are due to an ErrorExpr creeping into one of the C functions, which are all called goodB2G or bad, so seem to identify as the same function. However, the root cause of the ErrorExpr is unclear as there's nothing obvious in the extractor logs.

Questions:

  1. Are we ok with the DCA results? Should we merge this as-is?
  2. Change-note required? (Will fail PR-check)

Pull Request checklist

All query authors

Internal query authors only

  • Autofixes generated based on these changes are valid, only needed if this PR makes significant changes to .ql, .qll, or .qhelp files. See the documentation (internal access required).
  • Changes are validated at scale (internal access required).
  • Adding a new query? Consider also adding the query to autofix.

@github-actions github-actions bot added the C++ label Sep 16, 2024
@calumgrant calumgrant force-pushed the calumgrant/bmn/uninitialized-local branch from 2ee56cd to afdd26b Compare September 30, 2024 13:16
@calumgrant calumgrant changed the title Calumgrant/bmn/uninitialized local C++: Remove FPs from cpp/uninitialized-local when encountered extraction errors Sep 30, 2024
@calumgrant calumgrant marked this pull request as ready for review September 30, 2024 14:09
@calumgrant calumgrant requested a review from a team as a code owner September 30, 2024 14:09
@@ -0,0 +1 @@
Likely Bugs/Memory Management/UninitializedLocal.ql
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests for this query are located in cpp/test/query-tests/Security/CWE/CWE-457/semmle/tests. We should probably add the test case there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine by me.

@jketema
Copy link
Contributor

jketema commented Sep 30, 2024

The DCA results are interesting. We lose one FP on abseil (this is good), but also a bunch of results on the samate project, which are due to an ErrorExpr creeping into one of the C functions, which are all called goodB2G or bad, so seem to identify as the same function. However, the root cause of the ErrorExpr is unclear as there's nothing obvious in the extractor logs.

This is for the same reason as:

// The extractor deliberately emits an `ErrorExpr` as the first argument to
// the allocator call, if any, of a `NewOrNewArrayExpr`. That `ErrorExpr`
// should not be translated.
exists(NewOrNewArrayExpr new | expr = new.getAllocatorCall().getArgument(0))
or
not translateFunction(getEnclosingFunction(expr)) and
not Raw::varHasIRFunc(getEnclosingVariable(expr))

So we need to be a bit more careful here. We should probably define a predicate that captures this case and also use it in other queries that check for ErrorExpr.

@calumgrant
Copy link
Contributor Author

The DCA results are interesting. We lose one FP on abseil (this is good), but also a bunch of results on the samate project, which are due to an ErrorExpr creeping into one of the C functions, which are all called goodB2G or bad, so seem to identify as the same function. However, the root cause of the ErrorExpr is unclear as there's nothing obvious in the extractor logs.

This is for the same reason as:

// The extractor deliberately emits an `ErrorExpr` as the first argument to
// the allocator call, if any, of a `NewOrNewArrayExpr`. That `ErrorExpr`
// should not be translated.
exists(NewOrNewArrayExpr new | expr = new.getAllocatorCall().getArgument(0))
or
not translateFunction(getEnclosingFunction(expr)) and
not Raw::varHasIRFunc(getEnclosingVariable(expr))

So we need to be a bit more careful here. We should probably define a predicate that captures this case and also use it in other queries that check for ErrorExpr.

That certainly matches what I've observed. Thanks for the pointer.

@jketema
Copy link
Contributor

jketema commented Sep 30, 2024

Note that there's something similar going on with the first argument of a deallocator call of a delete expressions. And I'm now left wondering whether we handle that correctly in the IR (will briefly investigate).

@calumgrant calumgrant marked this pull request as draft September 30, 2024 16:16
@calumgrant
Copy link
Contributor Author

Note that there's something similar going on with the first argument of a deallocator call of a delete expressions. And I'm now left wondering whether we handle that correctly in the IR (will briefly investigate).

This does leave me wondering why this expression is emitted at all. I can work around it for now, but a follow-up could be to replace this with something else or avoid it entirely.

I've not looked into the IR, but it does seem to me that ignoreExprOnly(Expr expr) should also consider DeleteOrDeleteArrayExpr.

@jketema
Copy link
Contributor

jketema commented Oct 1, 2024

I've not looked into the IR, but it does seem to me that ignoreExprOnly(Expr expr) should also consider DeleteOrDeleteArrayExpr.

That only makes sense if the IR actually looks at that argument during IR generation, which I'm not sure of.

This does leave me wondering why this expression is emitted at all. I can work around it for now, but a follow-up could be to replace this with something else or avoid it entirely.

The expression is an argument to a call, so something needs to be there. Looking briefly at the extractor code it didn't seem completely straightforward to replace this with what it really should be.

@calumgrant calumgrant force-pushed the calumgrant/bmn/uninitialized-local branch from afdd26b to 4712ae1 Compare October 1, 2024 10:04
@jketema
Copy link
Contributor

jketema commented Oct 1, 2024

I've not looked into the IR, but it does seem to me that ignoreExprOnly(Expr expr) should also consider DeleteOrDeleteArrayExpr.

I was mistaken. We only output the error expression in the case of new, and not delete. The extractor code is pretty difficult to follow here.

Comment on lines 748 to 753
/**
* Holds if this error expression is the first argument to a `new` allocation call.
*/
predicate isFirstAllocatorCallArgument() {
this = any(NewOrNewArrayExpr new).getAllocatorCall().getArgument(0)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a particular fan of having this predicate, as it exposes something that shouldn't really be exposed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure about this name. You suggested I should encapsulate the functionality from TranslatedElement.qll, but I'm not sure how helpful this really is. Could also rename it to isExpected(), but it's still just a workaround really. Happy to remove this predicate and inline it as it's not really adding much value.

Copy link
Contributor

@jketema jketema Oct 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the misunderstanding, I did not suggest encapsulating the functionality from TranslatedElement.qll. I only tried to suggest to do something similar. I don't think we need this predicate for that. It's something that can just be inlined in the hasErrors predicate you introduced.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that would be cleaner.

@calumgrant
Copy link
Contributor Author

I've not looked into the IR, but it does seem to me that ignoreExprOnly(Expr expr) should also consider DeleteOrDeleteArrayExpr.

I was mistaken. We only output the error expression in the case of new, and not delete. The extractor code is pretty difficult to follow here.

I couldn't create a test case that generated the deallocator arg, so I decided to ignore it.

@jketema
Copy link
Contributor

jketema commented Oct 1, 2024

I couldn't create a test case that generated the deallocator arg, so I decided to ignore it.

Yup, because: "We only output the error expression in the case of new, and not delete."

@@ -89,5 +89,6 @@ from
where
conf.hasFlowPath(source, sink) and
isSinkImpl(sink.getInstruction(), va) and
v = va.getTarget()
v = va.getTarget() and
not v.getFunction().hasErrors()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might as well stick this constraint into isSinkImpl to speed up the recursion

@@ -57,5 +57,5 @@ where
not declarationHasSideEffects(v) and
not exists(AsmStmt s | f = s.getEnclosingFunction()) and
not v.getAnAttribute().getName() = "unused" and
not any(ErrorExpr e).getEnclosingFunction() = f // unextracted expr may use `v`
not f.hasErrors()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the comment should be retained here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or at least something along those lines.

@calumgrant calumgrant force-pushed the calumgrant/bmn/uninitialized-local branch from b4e9131 to 9ceca3e Compare October 1, 2024 15:11
@calumgrant calumgrant force-pushed the calumgrant/bmn/uninitialized-local branch from 9ceca3e to cd1f10c Compare October 1, 2024 15:15
Copy link
Contributor

@jketema jketema left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Just want to see some updated DCA results.

@calumgrant calumgrant marked this pull request as ready for review October 2, 2024 07:08
@calumgrant
Copy link
Contributor Author

Will add change note.

@calumgrant calumgrant merged commit 8b536f5 into main Oct 2, 2024
16 checks passed
@calumgrant calumgrant deleted the calumgrant/bmn/uninitialized-local branch October 2, 2024 08:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants