Fix #15000 (CheckCondition: detect compareValueOutOfTypeRange for bounded cast/arithmetic expressions) - #8819
Conversation
a1c4a25 to
8dc01be
Compare
8dc01be to
d37f6b1
Compare
| " if ((((sint64)((uint32)tmp)) - 1LL) < 0LL) {}\n" | ||
| " if ((((sint64)((uint32)tmp)) - 1LL) > 4294967295LL) {}\n" | ||
| "}\n", settingsUnix64); | ||
| ASSERT_EQUALS("[test.cpp:9:43]: (style) Comparing expression of type 'signed long long' against value 4294967295. Condition is always false. [compareValueOutOfTypeRangeError]\n", |
There was a problem hiding this comment.
The text/ID of the warning seems incorrect. 4294967295 is within range for signed long long.
There was a problem hiding this comment.
Hello! The condition is essentially (sint64), the diagnostic log is correct. But the check is only possible because there is a (uint32) inside.
There was a problem hiding this comment.
hmm it will at least be confusing. At first glance it sounds like 'signed long long' expressions shouldn't be compared against 4294967295.
It might be less confusing if the type 'signed long long' would be removed. The range of 'signed long long' expressions is much greater, the issue here is that the expression range corresponds to a 'uint32'. How problematic would that be to remove the type if the expression range does match the type range?
|
I have created this ticket: https://trac.cppcheck.net/ticket/15000 |
|
I will update the commit message with Fix #15000 ... |
d37f6b1 to
3e3fa63
Compare
| return false; | ||
|
|
||
| std::uint8_t bits = 0; | ||
| switch (tok->valueType()->type) { |
There was a problem hiding this comment.
maybe this switch could be simplified to something like
if (tok->valueType()->isIntegral())
bits = settings.platform.char_bit *
tok->valueType()->getSizeOf(settings, ValueType::Accuracy::ExactOrZero, ValueType::SizeOf::Pointee);
I think it would be shorter, duplicate less logic, and it would also support wchar_t
the check for isIntegral() could even be moved up to line 1980 to be very clear that this function only works for integral types
Cheers!
--Aaron
There was a problem hiding this comment.
Thanks for pointing to getSizeOf, it simplifies a lot.
| lower = -(MathLib::bigint(1) << (bits - 1)); | ||
| upper = max / 2; | ||
| } | ||
| } else { |
There was a problem hiding this comment.
enum Sign : std::uint8_t { UNKNOWN_SIGN, SIGNED, UNSIGNED } sign = UNKNOWN_SIGN;
I think if the sign is unknown you should probably return false, not treat it the same as an UNSIGNED
…ded cast/arithmetic expressions
3e3fa63 to
2d5f3a7
Compare
|
The PR title should be updated. The commit message will not be shown in the git log but the PR title will. |
Done. PR title updated to "Fix #15000 CheckCondition: detect compareValueOutOfTypeRange for bounded cast/arithmetic expressions" ; let me know if better convention would apply here. |
| if (tok->isCast() && tok->astOperand1()) { | ||
| MathLib::bigint typeLower; | ||
| MathLib::bigint typeUpper; | ||
| const bool hasSourceRange = getIntegerExpressionRange(tok->astOperand1(), settings, lower, upper); |
There was a problem hiding this comment.
c++ casts have two operands.
| return lower <= upper; | ||
| } | ||
|
|
||
| if (Token::simpleMatch(tok, "(") && tok->astOperand1()) |
There was a problem hiding this comment.
I am not sure what exact code you target here.
| MathLib::bigint constantLower; | ||
| MathLib::bigint constantUpper; | ||
| if (!getIntegerExpressionRange(tok->astOperand1(), settings, operandLower, operandUpper) || | ||
| !getIntegerExpressionRange(tok->astOperand2(), settings, constantLower, constantUpper) || |
There was a problem hiding this comment.
we will always return false for a unary +|- ?
| " if ((((sint64)((uint32)tmp)) - 1LL) < 0LL) {}\n" | ||
| " if ((((sint64)((uint32)tmp)) - 1LL) > 4294967295LL) {}\n" | ||
| "}\n", settingsUnix64); | ||
| ASSERT_EQUALS("[test.cpp:9:43]: (style) Comparing expression of type 'signed long long' against value 4294967295. Condition is always false. [compareValueOutOfTypeRangeError]\n", |
There was a problem hiding this comment.
hmm it will at least be confusing. At first glance it sounds like 'signed long long' expressions shouldn't be compared against 4294967295.
It might be less confusing if the type 'signed long long' would be removed. The range of 'signed long long' expressions is much greater, the issue here is that the expression range corresponds to a 'uint32'. How problematic would that be to remove the type if the expression range does match the type range?
Thanks! I don't have a strict pattern that must be followed but I typically put parentheses around the ticket title. Most important is that you indicated which ticket it solves.. |
| Certainty::normal); | ||
| } | ||
|
|
||
| static bool getIntegerTypeRange(const Token* tok, |
There was a problem hiding this comment.
I have the feeling we have a utility function to determine min/max values of an integer type.
| return true; | ||
| } | ||
|
|
||
| static bool getIntegerExpressionRange(const Token* tok, |
There was a problem hiding this comment.
have you checked if there is some similar utility function somewhere?
| } | ||
|
|
||
| static bool getIntegerExpressionRange(const Token* tok, | ||
| const Settings& settings, |
There was a problem hiding this comment.
I don't have a strong opinion but passing settings is overkill we could just pass a platform.
| { | ||
| if (!tok) | ||
| return false; | ||
| if (tok->hasKnownIntValue()) { |
There was a problem hiding this comment.
if there is not a known value.. I think it would be good to consider the "impossible" values.
Example:
void foo(uint32_t x) {
if (x > 100) return;
a = (uint64_t)x;
}
In the cast the values for token x are (this is the --debug output):
x {!<=-1,!>=101,<=100}
The !>=101 means x cannot have values 101 and more.
|
I have a feeling that some of the logic should be performed in ValueFlow and not in the check. Currently, ValueFlow only seems to handle |
There was missing detection of 'MISRA C:2012 Rule 14.3" on some specific code structure generated by Matlab Simulink coder.
We identified these issues by comparing its results with reports previously generated by our company’s internal tool on the same software.
The bug report would have looked like below.
Problem
Cppcheck did not report a MISRA C:2012 Rule 14.3 violation when a comparison
used an integer expression whose range was provably bounded by casts and
constant arithmetic. The existing check handled a variable compared with a
value outside the variable's declared type range, but did not evaluate this
equivalent expression form.
Minimal Reproducer
After the clamp and casts, the left-hand expression cannot exceed
UINT32_MAX - 1. Therefore the final condition is always false and shouldproduce the
compareValueOutOfTypeRangeErrordiagnostic, which the MISRApostprocessor maps to Rule 14.3.
Observed Behavior
Before the fix, the final comparison produced no diagnostic. This allowed the
generated-code pattern to pass through the cppcheck-based MISRA pipeline
without the expected Rule 14.3 finding.
Fix
The condition checker now:
or subtraction by constant expressions;
provably always true or always false; and
compareValueOutOfTypeRangeErrordiagnostic path.Uncertain or unsupported ranges remain unreported to avoid speculative
findings. Interval arithmetic includes overflow guards.
Regression Coverage
The exact typedef-based reproducer is covered by
TestCondition::compareOutOfTypeRangeintest/testcondition.cpp. The focusedtest, the complete
TestConditionsuite, and the registered CTest target passwith the fix.