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
C++: failing test for a weird range analysis #7396
base: main
Are you sure you want to change the base?
Conversation
Bounded variables (once by a for loop, then even more bounded by an if) seem to throw off range analysis if a subtraction (even with 0) is done on them. Notice that any of: * not having the outer for loop (even replacing it with an if making the same bounds), or * not subtracting will not show the false positive.
| if (1 <= i && i <= 10) { | ||
| sprintf(buffer, "%d", i - 1); // GOOD, 0 <= i - 1 <= 9 | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not quite sure what the problem is. Did you mean to show a false positive case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes this is a weird FP. What seems weird to me is that i - 0 has a different range being found out than i alone (removing - 0 makes range analysis find the correct range here), or that the analysis works correctly also if we do
void test7(int i) {
char buffer[2];
if (0 <= i && i <= 11) {
if (i <= 9) {
sprintf(...);There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added the current test results together with the equivalent thing in library tests, I'll comment there
| if (1 <= i && i <= 10) { | ||
| sprintf(buffer, "%d", i - 1); // GOOD, 0 <= i - 1 <= 9 | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added the current test results together with the equivalent thing in library tests, I'll comment there
| | weird.c:5:15:5:15 | i | 127 | | ||
| | weird.c:5:26:5:26 | i | 15 | | ||
| | weird.c:6:14:6:14 | i | 11 | | ||
| | weird.c:6:19:6:19 | i | 15 | | ||
| | weird.c:7:11:7:11 | i | 9 | | ||
| | weird.c:9:7:9:7 | j | 2147483647 | | ||
| | weird.c:9:11:9:11 | i | 9 | | ||
| | weird.c:10:11:10:11 | j | 15 | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here's what puzzles me: although i gets the correct upper bound of 9 on weird.c:9, and j is assigned i - 0, j actually gets an upper bound of 15.
I also have no idea where the 127 and 15 bounds for i come from in line 5 (for loop) and line 6 (if). Also notice that there is nothing similar going on on the lower bounds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uh, I just noticed in test.c
void widen_recursive_expr() {
int s;
for (s = 0; s < 10; s++) {
int result = s + s; // 0 .. 9 [BUG: upper bound is 15 due to widening]
out(result); // 0 .. 18 [BUG: upper bound is 127 due to double widening]
}
}is this mentioning exactly what is going on here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I read the widening explanation on SimpleRangeAnalysis.qll, which at least explains to me where the 15 and 127 bounds come from (and also why I see no such thing on the lower bounds, it's just that 0 is in the finite set of approximations of lower bounds).
Still, I feel that limitation should not really apply here: j = i - 0 comes after a spot where the correct bounds for i have been found, so why does it fall back to the widening finite set of approximations for j?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still, I feel that limitation should not really apply here: j = i - 0 comes after a spot where the correct bounds for i have been found, so why does it fall back to the widening finite set of approximations for j?
This is, unfortunately, the expected behavior from the library. I'll explain the reason below. Let me paste in the problematic snippet here to aid the explanation:
void weird1() {
int i;
for (i = 0; i <= 11; ++i) {
if (0 <= i && i <= 9) {
out(i);
int j;
j = i - 0;
out(j);
}
}
}The TLDR is that we deduce the bound [0, 15] for j because the assignment to j depends on the recursively defined variable i (i is recursive because of the ++i in the loop header). The long version is the following:
- The main loop of the range-analysis recursion (
getUpperBoundsImpl) delegates togetDefUpperBoundsfor a definitions likej = i - 0. getDefUpperBoundsdelegates togetDefUpperBoundsImpl.getDefUpperBoundsImplusesassignmentDefto deduce that the right-hand-side ofj = i - 0isi - 0, and we delegate togetFullyConvertedUpperBoundsto deduce an upper bound fori - 0.getFullyConvertedUpperBoundscallsgetTruncatedUpperBoundsto factor in implicit conversions oni - 0(which aren't relevant in this case).getTruncatedUpperBoundscallsgetUpperBoundsImplto get an upper bound fori - 0.getUpperBoundsImplenters the case forSubExpr, which callsgetFullyConvertedUpperBoundson the left-hand side (i)getFullyConvertedUpperBoundscallsgetTruncatedUpperBoundsto factor in implicit conversions oni(again, which aren't relevant in this case).getTruncatedUpperBoundscallsgetUpperBoundsImplto get an upper bound onigetUpperBoundsImplenters theSSAcase, and callsgetDefUpperBoundswith the definition for i which in this case is++i. This is a recursive definition, and so the upper bound foriis widened to15.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I need to wrap my head around it a bit, but I can already ask this: why is i on line 7 not affected by this (or the i access on in the assignment to j as well)? The way I see it, there is the approximated, widened bound given by the loop, but then there is a stricter bound imposed by the if. How it is that this stricter bound is applied on a direct access to i, but not on an expression containing i?
Bounded variables (once by a
forloop, then even more bounded by anif) seem to throw off range analysis if a subtraction (even with 0) is done on them.Notice that any of:
will not show the false positive.
The text was updated successfully, but these errors were encountered: