Skip to content

C++: Flow out of invalid functions #12005

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,20 @@ class FinalParameterUse extends UseImpl, TFinalParameterUse {
override predicate isCertain() { any() }

override predicate hasIndexInBlock(IRBlock block, int index) {
exists(ReturnInstruction return |
// Ideally, this should always be a `ReturnInstruction`, but if
// someone forgets to write a `return` statement in a function
// with a non-void return type we generate an `UnreachedInstruction`.
// In this case we still want to generate flow out of such functions
// if they write to a parameter. So we pick the index of the
// `UnreachedInstruction` as the index of this use.
// Note that a function may have both a `ReturnInstruction` and an
// `UnreachedInstruction`. If that's the case this predicate will
// return multiple results. I don't think this is detrimental to
// performance, however.
exists(Instruction return |
return instanceof ReturnInstruction or
return instanceof UnreachedInstruction
|
block.getInstruction(index) = return and
return.getEnclosingFunction() = p.getFunction()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,21 @@ private class FinalParameterUse extends UseImpl, TFinalParameterUse {
override string toString() { result = p.toString() }

final override predicate hasIndexInBlock(IRBlock block, int index) {
exists(ReturnInstruction return |
block.getInstruction(index + 1) = return and
// Ideally, this should always be a `ReturnInstruction`, but if
// someone forgets to write a `return` statement in a function
// with a non-void return type we generate an `UnreachedInstruction`.
// In this case we still want to generate flow out of such functions
// if they write to a parameter. So we pick the index of the
// `UnreachedInstruction` as the index of this use.
// Note that a function may have both a `ReturnInstruction` and an
// `UnreachedInstruction`. If that's the case this predicate will
// return multiple results. I don't think this is detrimental to
// performance, however.
exists(Instruction return |
return instanceof ReturnInstruction or
return instanceof UnreachedInstruction
|
block.getInstruction(index) = return and
Copy link
Contributor

Choose a reason for hiding this comment

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

index + 1 has been replaced here by index. Was this incorrect before?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, good question. It wasn't actually incorrect. The only thing we require is that it's the last use of the parameter in the body of the function. ssa0/SsaInternals.qll did index + 1, and SsaInternals.qll did index. So I just synced them up to be index for consistency. This will hopefully make it easier to merge those two files into one parameterized module eventually.

Copy link
Contributor Author

@MathiasVP MathiasVP Jan 27, 2023

Choose a reason for hiding this comment

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

index + 1 might actually be problematic since we can generate IR where the UnreachedInstruction is the only thing in the block. So there wouldn't necessarily be any instruction at block.getInstruction(index + 1).

This is different from ReturnInstructions since we know there is always a ExitFunctionInstruction (or whatever it's called) after the ReturnInstructions.

return.getEnclosingFunction() = p.getFunction()
)
}
Expand Down
2 changes: 1 addition & 1 deletion cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,7 +510,7 @@ int intOutparamSourceMissingReturn(int *p) {
void viaOutparamMissingReturn() {
int x = 0;
intOutparamSourceMissingReturn(&x);
sink(x); // $ ast MISSING: ir
sink(x); // $ ast,ir
}

void uncertain_definition() {
Expand Down