C++: Modernize MustFlow and fix allowInterproceduralFlow in the case of direct recursion#21331
C++: Modernize MustFlow and fix allowInterproceduralFlow in the case of direct recursion#21331jketema merged 7 commits intogithub:mainfrom
MustFlow and fix allowInterproceduralFlow in the case of direct recursion#21331Conversation
MustFlow using parameterized modulesMustFlow and fix allowInterproceduralFlow in the case of direct recursion
There was a problem hiding this comment.
Pull request overview
Modernizes the C++ MustFlow CodeQL library to parameterized modules and fixes allowInterproceduralFlow behavior for direct recursion, updating dependent queries and adding a regression test.
Changes:
- Reworked
MustFlowintoMustFlow::ConfigSig+MustFlow::Global<...>and updated queries to use module instances and instance-specificPathGraph. - Adjusted interprocedural stepping so
allowInterproceduralFlow() { none() }blocks direct recursion. - Added a C++ test case covering direct recursion for return-of-stack-address analysis.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp | Adds regression test for direct recursion case. |
| cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql | Migrates query to new MustFlow::Global module instantiation and updated path node types. |
| cpp/ql/src/Likely Bugs/Memory Management/UninitializedLocal.ql | Migrates query to new MustFlow module API, including instance-specific PathGraph. |
| cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql | Migrates query to new MustFlow module API and updates path problem wiring. |
| cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll | Major refactor: introduces MustFlow::ConfigSig and MustFlow::Global, adjusts interprocedural recursion handling. |
| cpp/ql/lib/change-notes/2026-02-14-must-flow.md | Documents the breaking API change for MustFlow. |
| cpp/ql/lib/change-notes/2026-02-14-must-flow-fix.md | Documents the recursion-related fix in must-flow configs. |
| * The corresponding paths are generated from the end-points and the graph | ||
| * included in the module `PathGraph`. | ||
| */ | ||
| predicate flowPath(PathNode source, PathSink sink) { |
There was a problem hiding this comment.
flowPath is exported but its signature exposes PathSink, which is a private class. This makes the public API awkward for consumers (they can’t name PathSink in their own type declarations). Consider changing flowPath to accept PathNode for the sink parameter and enforce sink instanceof PathSink in the body, or make PathSink non-private (and document it as part of the API).
| predicate flowPath(PathNode source, PathSink sink) { | |
| predicate flowPath(PathNode source, PathNode sink) { | |
| sink instanceof PathSink and |
| private class PathSink extends PathNode { | ||
| PathSink() { isSink(this.getInstruction().getAUse()) } | ||
| } |
There was a problem hiding this comment.
flowPath is exported but its signature exposes PathSink, which is a private class. This makes the public API awkward for consumers (they can’t name PathSink in their own type declarations). Consider changing flowPath to accept PathNode for the sink parameter and enforce sink instanceof PathSink in the body, or make PathSink non-private (and document it as part of the API).
geoffw0
left a comment
There was a problem hiding this comment.
CodeQL changes and DCA LGTM.
| int x; | ||
| int* px = f_rec(&x); // GOOD | ||
| return p; | ||
| } |
There was a problem hiding this comment.
This function is a bit odd because it never returns, so of course it doesn't return stack allocated memory. I don't think that's the point of the test though - can we make it return, e.g. by adding an iteration count?
There was a problem hiding this comment.
Note that conditional stuff also means there is no longer a must-flow relation. So even before the changes from this PR the following does not produce an alert:
int* f_rec_n(int *p, int n) {
int x;
if (n != 0)
int* px = f_rec(&x, n - 1);
return p;
}There was a problem hiding this comment.
So I think we should leave the test as-is, even though it looks "wrong".
There was a problem hiding this comment.
Good point. I was a bit concerned about (1) readability and (2) if at some point we excluded dead code from the query, this test would no longer cover what it's supposed to (because return p is simply dead code). But given what you've said above, a fix for (2) is likely to make (1) worse.
Lets leave it as it is then.
MustFlowlibrary over to parameterized modules. Note that this is a breaking change. Since updating uses of the library should be easy, and probably not many uses exist, I opted for not keeping around the "configuration class" version as deprecated.allowInterproceduralFlowin the case of direct recursion. This fixes MustFlowConfiguration::allowInterproceduralFlow doesn't work for recursive functions #21240Commit-by-commit review is recommended.