Repository navigation
Fix #12612 FP uninitvar with pointer alias in subfunction - #7249
chrchr-github wants to merge 5 commits into
Conversation
| return values.count(tok->varId()) > 0 || | ||
| std::any_of(values.begin(), values.end(), [&](const std::pair<nonneg int, ValueFlow::Value>& p) { | ||
| return p.second.isUninitValue() && p.second.tokvalue->varId() == tok->varId(); | ||
| }); |
There was a problem hiding this comment.
Why is this extra check needed?
There was a problem hiding this comment.
Which check exactly? We now match in case there is an alias that has an uninit value pointing to the variable in question. But I'm not sure if it is correct to only check for uninit values.
There was a problem hiding this comment.
That should happen in the analyzeToken function, not here. We are already checking for aliases there as well.
Finally the tokvalue is not always an alias either.
There was a problem hiding this comment.
analyzeToken() has this:
Action la = analyzeLifetime(lifeTok);
if (la.matches()) { ...So matches() needs to return true for the propagation of the uninit value to stop.
I looked at how that works for local code and tried to make the same stuff happen with a subfunction/valueFlowInjectParameter()
There was a problem hiding this comment.
So matches() needs to return true for the propagation of the uninit value to stop.
Its not that matches() needs to return true, but that analyzeLifetime should return Action::Match, but I also think it can return Action::Read as well as the isAliasModified will handle it for cases where its not an exact match.
So it seems like you are modifying match so analyzeLifetime returns Action::Match, which is not the right approach. The match function is for exact matches, not to match aliases. That can lead to FPs. This needs to be done in either analyzeToken or analyzeLifetime where we are already checking for aliases.
There was a problem hiding this comment.
MultiValueFlowAnalyzer::values is inaccessible in analyzeLifetime()and analyzeToken().
There was a problem hiding this comment.
Why do you need to read the values? The lifetime values are stored in the token directly.
There was a problem hiding this comment.
For this case, p has the lifetime of lifetime[Object]=(a)@1, so analyzeLifetime should return Action::Match.
There was a problem hiding this comment.
Ah wait, I see the issue. MultiValueFlowAnalyzer uses the variables ids of the parameters not the arguments passed.
We probably need to pass a map to MultiValueFlowAnalyzer that takes the exprid of the expression passed and the Variable of the function parameter. Something like:
std::unordered_map<nonneg int, ValueFlow::Value> values;
std::unordered_map<nonneg int, const Variable*> vars;
std::unordered_map<nonneg int, const Variable*> ivars;
MultiValueFlowAnalyzer(const std::unordered_map<const Variable*, ValueFlow::Value>& args, const std::unordered_map<nonneg int, const Variable*>& iargs, const Settings& set)
: ValueFlowAnalyzer(set) {
...
}
Then the match function can check the vars and ivars. We need to update getValue to check ivars as well.
There was a problem hiding this comment.
You seem to have an idea how to implement this, do you want to take over?
| const Token* lifeTok = nullptr; | ||
| for (const ValueFlow::Value& v:ref->astOperand1()->values()) { | ||
| if (!v.isLocalLifetimeValue()) | ||
| if (!v.isLocalLifetimeValue() && !v.isSubFunctionLifetimeValue()) |
There was a problem hiding this comment.
Instead of skipping subfunction lifetimes, you could just check that the path is the same if the propagated value's path(ie the value returned from getValue(lifetok)) is not zero.
There was a problem hiding this comment.
Subfunction lifetimes were skipped before this change, and we didn't proceed to analyzeLifetime() below.
There was a problem hiding this comment.
I see, I read it backwards. Its enabling it. We still need to check the correct path to avoid FPs.
|
|
|
@pfultz2 since you reviewed before .. I would really appreciate your eyes on this again.. |
|
I think test-my-pr.py would be good to run. However it currently does not work very well. I believe after I merge #8491 it will work better.. |
|
I asked Claude to review this. Feel free to ignore comments.. but here is what Claude had to say: Hunk 2 — vf_analyzers.cpp:1074 (MultiValueFlowAnalyzer::match)Change: Extend match() to also return true when any tracked value is UNINIT and its tokvalue->varId() equals the queried token's varId. Concerns:
Test coverageOne test case is added, which covers the minimal reproduction. Suggested additions:
|
|
A testrun would be interesting. If you rebase then the test-pr should pick up this PR. |
dmcppcheck
left a comment
There was a problem hiding this comment.
AI review
The fix only works in a narrow case. When an array is passed to the subfunction, an FP that differs only slightly from the new test is still reported: int f(char *b) { char *p = b; *p = 0; return *b; } called with char a[24] still gives Uninitialized variable: *b. The same happens with int a[2] and with p[0] = 0. The new test passes only because it returns b and doesn't dereference it. The fix does work for a scalar passed by address (int a; f(&a);) and for a struct (p->x = 0; return b->x;).
An alias chain (char *q = p; *q = 0; return *b;) also still gives the FP, and the PR has no test that checks the warning still fires when the write through the alias is missing. Both points were already suggested in comments.md, so I only confirm here that they are still open.
Both hunks are needed: with only the analyzeToken hunk, the result for every case I tried is the same as base. I found no new false positives or crashes. The design and null-tokvalue concerns about match() are already in the thread.
Testing
I built cppcheck and testrunner in pr/ and cppcheck in base/. Both built without errors.
./testrunner TestUninitVar TestValueFlow TestNullPointer: 300 tests, 0 failures.- Full
./testrunner -q: 5387 tests, 0 failures. - I also built a copy of pr/ with only hunk 1 (the
analyzeTokenchange) and the originalmatch(). It gives exactly the same results as base on all my cases, so hunk 2 is needed.
FP that remains for array arguments (scratch/t4.c and scratch/t2.c):
int f1(char *b) { char* p = b; *p = 0; return *b; }
void g1() { char a[24]; f1(a); }
int f2(char *b) { char* p = b; p[0] = 0; return *b; }
void g2() { char a[24]; f2(a); }
int f3(int *b) { int *p = b; *p = 0; return *b; }
void g3() { int a[2]; f3(a); }
int f4(int *b) { int *p = b; *p = 0; return *b; } // fixed by the PR
void g4() { int a; f4(&a); }I ran cppcheck -q --enable=warning t.c.
Output with the PR (base gives the same warnings plus one for f4):
t.c:1:47: warning: Uninitialized variable: *b [uninitvar]
t.c:3:49: warning: Uninitialized variable: *b [uninitvar]
t.c:5:45: warning: Uninitialized variable: *b [uninitvar]
FP that remains for an alias chain:
int f3(char *b) { char* p = b; char *q = p; *q = 0; return *b; }
void g3() { char a[24]; f3(a); }Output with the PR, the same as base:
warning: Uninitialized variable: *b [uninitvar]
Cases where the PR keeps the correct warnings:
- Two uninit arguments, writing through an alias of one and reading the other.
- A conditional write.
p++/p = b + 1before the write.
Case that the PR fixes: a struct member written through the alias (p->x = 0; return b->x;).
Automated review of cf9ac5e by Claude. The pull request was built and tested locally.
|
|
||
| valueFlowUninit("char *f (char *b) {\n" // #12612 | ||
| " char* p = b;\n" | ||
| " *p = '\\0';\n" |
There was a problem hiding this comment.
AI review
This test passes only because the function returns b and doesn't dereference it. With a dereferenced read, the array case still gives the FP with this PR:
char f (char *b) { char* p = b; *p = '\0'; return *b; }
void g() { char a[24]; f(a); }Output: warning: Uninitialized variable: *b [uninitvar]. The same happens with int a[2] and with p[0] = 0. The fix only works for int a; f(&a);. Please add the return *b; array variant, and either fix it or mark it as a TODO.
|
Test results for commit cf9ac5e (tools/test-my-pr.py, main compared to this PR): Test: http://ec2-16-170-140-253.eu-north-1.compute.amazonaws.com/pr-7249/ Posted automatically by the cppcheck PR test runner. +N: warnings only with this PR, -N: warnings only with main. The AI review is written by Claude and can be wrong. |



No description provided.