Repository navigation
Fix #15034 FN arrayIndexOutOfBounds (ternary in subfunction) - #8856
chrchr-github wants to merge 10 commits into
Conversation
|
|
||
| // The struct Fred has two functions, a constructor and a destructor | ||
| ASSERT_EQUALS(2U, fredScope->functionList.size()); | ||
| ASSERT_EQUALS(2U, fredScope->functionList.size()); // cppcheck-suppress nullPointer // see ticket #9747 |
There was a problem hiding this comment.
This is an AI review. Take it with a grain of salt and feel free to reject it by resolving the comment.
I confirmed that this suppression is needed because of the PR. With selfcheck's options (--check-level=exhaustive --library=cppcheck-lib ...), the merge base gives no warning here, while this PR gives Possible null pointer dereference: fredScope. It is a false positive, since assert_() throws when the condition fails. The cause is that the removed "condition depends on only one variable" guard in setTokenValue() used to drop the nullptr from (it == db->scopeList.end()) ? nullptr : &*it, because the condition contains a function call. So user code with the same "ternary + throwing check" pattern will now also hit #9747.
To get a feel for how often that happens, I compared warnings for the merge base and the PR on lib/, cli/, test/cfg/, samples/, testsymboldatabase.cpp and testvalueflow.cpp (normal check level, style/warning/portability/performance, inconclusive). Both gave exactly the same 2407 warnings, so it doesn't look widespread. Some correlated-ternary probes such as d = (a > b) ? 0 : a - b; if (a > b) return 0; return 10 / d; also stayed silent. Just mentioning it so the trade-off is a conscious one.
|
This pull request has merge conflicts that must be resolved. |
|
@claude review |
| if (std::find(values.cbegin(), values.cend(), value) != values.cend()) | ||
| setTokenValue(parent, std::move(value), settings); | ||
| } | ||
| } else if (!value.isImpossible()) { |
There was a problem hiding this comment.
Removing the "condition depends on only 1 variable / no function call" guard entirely means any possible value from either branch now goes up to ? whatever the condition is (several variables, function calls, values that carry a varId). That's a lot wider than what #15034 needs, so I'd expect new false positives (nullPointer, arrayIndexOutOfBounds, zerodiv) downstream. The selfcheck suppression added in testsymboldatabase.cpp suggests this has already happened. Could the guard be relaxed just for the case in the ticket, where the branch values come from the same variable that's in the condition, instead of being dropped? Please also add some negative tests, e.g. x ? y : 0 where y has a possible value that's unrelated to x.
|
|
||
| // The struct Fred has two functions, a constructor and a destructor | ||
| ASSERT_EQUALS(2U, fredScope->functionList.size()); | ||
| ASSERT_EQUALS(2U, fredScope->functionList.size()); // cppcheck-suppress nullPointer // see ticket #9747 |
There was a problem hiding this comment.
This suppression is only needed because of the valueflow change, so selfcheck now reports a new nullPointer false positive (after ASSERT(fredScope != nullptr)). I'd fix the regression rather than suppress it here. Users will hit the same pattern.
| ASSERT_EQUALS_ENUM(ValueFlow::Value::ValueType::INT, it->valueType); | ||
| ASSERT_EQUALS(5, it->intvalue); | ||
| ASSERT_EQUALS_ENUM(ValueFlow::Value::Bound::Point, it->bound); | ||
| ASSERT_EQUALS_ENUM(ValueFlow::Value::ValueKind::Possible, it->valueKind); |
There was a problem hiding this comment.
The ticket is about an arrayIndexOutOfBounds false negative, but there's no checker-level test for it. Please add the ticket example to testbufferoverrun.cpp, together with a negative case such as a[i < 0 ? 0 : i] called with g(-5), which must not warn.
|
Summary:
🤖 Generated with Claude Code |
|
Test results for commit 72fa075 (tools/test-my-pr.py, main compared to this PR): Test: http://ec2-16-170-140-253.eu-north-1.compute.amazonaws.com/pr-8856/ 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. |
dmcppcheck
left a comment
There was a problem hiding this comment.
AI review
I found no new correctness problems. In my tests the current version of the change (only dropping value.varId != 0 from the guard in setTokenValue(), plus inferring only for the LHS of ?) gave no new false positives. It also fixes a wrong base value: in c = (a > 0) ? a : -a base gave c >= 1, while the PR gives the correct c >= 0.
One new remark, about the test: the second added valueFlowTernary case is invalid code because it redeclares the parameter a. Its two assertions check the same fields and leave out the symbol, so they can't tell the two values apart and pin down little. Details are in the inline comment.
The missing checker-level test in testbufferoverrun.cpp and the wider propagation concern have already been raised in comments.md, so I don't repeat them.
Testing
Build: make -j8 cppcheck testrunner in pr/ and make -j8 cppcheck in base/. Both built cleanly.
Unit tests: ./testrunner TestValueFlow TestSymbolDatabase TestBufferOverrun TestNullPointer TestUninitVar TestOther TestCondition ran 1253 tests with 0 failures.
Ticket case (int a[5]; int g(int i){ return a[i < 0 ? -i : i]; } int f(void){ return g(-5); }): base is silent. The PR reports arrayIndexOutOfBounds at index 5 with a correct "Assuming condition 'i<0' is true" note.
Probes, base vs PR (cppcheck -q --enable=warning), all in /work/scratch:
- New true positives with the PR:
a[i > 0 ? i : -i]withg(-7)a[i < 0 ? -i : (i > 9 ? 0 : i)]withg(-6)
- No false positives on the excluded branch, in either base or PR:
a[i < 0 ? 0 : i + 5]withg(-1)and withg(1)a[i > 4 ? 4 : i],a[i != 10 ? i : 0],a[!(i<5) ? 0 : i],a[(i & 8) ? 0 : i],a[i*2 > 8 ? 0 : i],a[(i>4||i<0) ? 0 : i], each with an out-of-range argumenta[(unsigned)i < 5u ? i : 4]withg(-1)100 / (d ? d : 1)and100 / (d == 0 ? 1 : d)withg(0)
- Correlated conditions on different variables (the case I expected to regress, since
Token::addValuegives every value on a variable token avarId): in all of these base and PR gave the same output.ok ? i : 0,n > 4 ? 0 : i,x > 0 ? i : 0afterif (x > 0) i = 1;,x ? d : 1,n > 0 ? q : py = x*2; a[x < 2 ? y : 0]- for-loop variables in a branch
- global, member and parameter conditions
- Larger run: I ran base and PR over
lib/{valueflow,astutils,token,tokenize,checkother,checkcondition,programmemory}.cpp,test/cfgandsampleswith warning, style and portability enabled, inconclusive, and--library=cppcheck-lib. Both gave the same 2376 warnings. - Second new test's code (debug value flow): in the PR,
:no longer gets the bogus value1from the old?-RHS inference, anda/cbecome>=0instead of the incorrect>=1in base. cppcheck also reportslegacyUninitvaron that snippet because of theint aredeclaration, in both base and PR.
Not run: the selfcheck and the 1000-package comparison. The PR test-runner numbers in comments.md are for an older commit (72fa075), before the later vf_settokenvalue.cpp changes.
Automated review of 7ae3a34 by Claude. The pull request was built and tested locally.
| code = "int f(int a, int b) {\n" | ||
| " if (b <= 0)\n" | ||
| " return b;\n" | ||
| " int a = (a > 0) ? a : -a;\n" |
There was a problem hiding this comment.
AI review
This snippet isn't valid C/C++: int a = (a > 0) ? a : -a; redeclares the parameter a in the function's outermost block, and it initialises a from itself. Running cppcheck on it gives legacyUninitvar on line 4. Please use a fresh variable, e.g. int c = (a > 0) ? a : -a; return 1 / (c < b ? b : c);, which gives the same value-flow shape.
Also, the two values below are asserted with identical fields (SYMBOLIC, -1, Upper, Impossible), and it->tokvalue is never checked. That means the test can't tell the !<=symbolic=(a-1) value from the !<=symbolic=(b-1) value. Checking the symbol, e.g. ASSERT_EQUALS("b", it->tokvalue->expressionString()), or using isImpossibleContainerSizeValue-style helpers would make the test pin down the intended behaviour.
|
Test results for commit 7ae3a34 (tools/test-my-pr.py, main compared to this PR): Test: http://ec2-16-170-140-253.eu-north-1.compute.amazonaws.com/pr-8856/ 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.