-
Notifications
You must be signed in to change notification settings - Fork 271
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
__CPROVER_forall
fails when dealing with flattening of nested structure
#8570
Comments
Taking a look. Here is a simplified version that equally fails and neither uses contracts nor requires the use of an SMT solver - typedef struct __attribute__((packed)) {
int data[2];
} arr;
typedef struct __attribute__((packed)) {
arr vec[2];
} arrvec;
int main() {
arrvec A;
arrvec *x = &A;
__CPROVER_assume(x->vec[1].data[0] < 42);
// OK:
__CPROVER_assert(((int*)x)[2] < 42, "");
// NOT OK:
__CPROVER_assert(__CPROVER_forall {unsigned k; k == 2 ==> ((int*)x)[k] < 42}, "");
// OK:
__CPROVER_assert(__CPROVER_forall {unsigned k; k == 2 ==> ((int (*)[2])x)[k/2][k % 2] < 42}, "");
} |
Turns out this isn't a problem related to quantifiers. The following further simplification (still equivalent to the original one) also fails: typedef struct __attribute__((packed)) {
int data[2];
} arr;
typedef struct __attribute__((packed)) {
arr vec[2];
} arrvec;
int main() {
arrvec A;
arrvec *x = &A;
__CPROVER_assume(x->vec[1].data[0] < 42);
unsigned k;
__CPROVER_assert(k != 2 || ((int*)x)[k] < 42, "");
} It seems the problem is with field sensitivity during symbolic execution, where we appear to (spuriously) turn |
Previously, runtime assertions via debug_assert_xxx and CBMC assertions via cassert(...) were separate. This commit modifies the implementation of the debug assertion macros so that when CBMC is used, debug assertions are intepreted as proof obligations. This removes some redundancy and non-uniformity in the code, and also reduces the likelihood that debug assertions and CBMC contracts get out of sync. In some case, this actually happened, and the commit fixes this. The commit also adds further bounds assertions in alignment with pre/post conditions. A slight nuisance is that the debug assertions cannot flatten nested structures like polyvec for the bounds check, running into issue diffblue/cbmc#8570. We work around this by introducing a new `xxx_2d` (for 2-dimensional) macro which takes two dimensions and uses a two-step array access, circumventing the above CBMC issue. Signed-off-by: Hanno Becker <[email protected]>
Previously, runtime assertions via debug_assert_xxx and CBMC assertions via cassert(...) were separate. This commit modifies the implementation of the debug assertion macros so that when CBMC is used, debug assertions are intepreted as proof obligations. This removes some redundancy and non-uniformity in the code, and also reduces the likelihood that debug assertions and CBMC contracts get out of sync. In some case, this actually happened, and the commit fixes this. The commit also adds further bounds assertions in alignment with pre/post conditions. A slight nuisance is that the debug assertions cannot flatten nested structures like polyvec for the bounds check, running into issue diffblue/cbmc#8570. We work around this by introducing a new `xxx_2d` (for 2-dimensional) macro which takes two dimensions and uses a two-step array access, circumventing the above CBMC issue. Signed-off-by: Hanno Becker <[email protected]>
Previously, runtime assertions via debug_assert_xxx and CBMC assertions via cassert(...) were separate. This commit modifies the implementation of the debug assertion macros so that when CBMC is used, debug assertions are intepreted as proof obligations. This removes some redundancy and non-uniformity in the code, and also reduces the likelihood that debug assertions and CBMC contracts get out of sync. In some case, this actually happened, and the commit fixes this. The commit also adds further bounds assertions in alignment with pre/post conditions. A slight nuisance is that the debug assertions cannot flatten nested structures like polyvec for the bounds check, running into issue diffblue/cbmc#8570. We work around this by introducing a new `xxx_2d` (for 2-dimensional) macro which takes two dimensions and uses a two-step array access, circumventing the above CBMC issue. Signed-off-by: Hanno Becker <[email protected]>
Previously, runtime assertions via debug_assert_xxx and CBMC assertions via cassert(...) were separate. This commit modifies the implementation of the debug assertion macros so that when CBMC is used, debug assertions are intepreted as proof obligations. This removes some redundancy and non-uniformity in the code, and also reduces the likelihood that debug assertions and CBMC contracts get out of sync. In some case, this actually happened, and the commit fixes this. The commit also adds further bounds assertions in alignment with pre/post conditions. A slight nuisance is that the debug assertions cannot flatten nested structures like polyvec for the bounds check, running into issue diffblue/cbmc#8570. We work around this by introducing a new `xxx_2d` (for 2-dimensional) macro which takes two dimensions and uses a two-step array access, circumventing the above CBMC issue. Signed-off-by: Hanno Becker <[email protected]>
Previously, runtime assertions via debug_assert_xxx and CBMC assertions via cassert(...) were separate. This commit modifies the implementation of the debug assertion macros so that when CBMC is used, debug assertions are intepreted as proof obligations. This removes some redundancy and non-uniformity in the code, and also reduces the likelihood that debug assertions and CBMC contracts get out of sync. In some case, this actually happened, and the commit fixes this. The commit also adds further bounds assertions in alignment with pre/post conditions. A slight nuisance is that the debug assertions cannot flatten nested structures like polyvec for the bounds check, running into issue diffblue/cbmc#8570. We work around this by introducing a new `xxx_2d` (for 2-dimensional) macro which takes two dimensions and uses a two-step array access, circumventing the above CBMC issue. Signed-off-by: Hanno Becker <[email protected]>
Previously, runtime assertions via debug_assert_xxx and CBMC assertions via cassert(...) were separate. This commit modifies the implementation of the debug assertion macros so that when CBMC is used, debug assertions are intepreted as proof obligations. This removes some redundancy and non-uniformity in the code, and also reduces the likelihood that debug assertions and CBMC contracts get out of sync. In some case, this actually happened, and the commit fixes this. The commit also adds further bounds assertions in alignment with pre/post conditions. A slight nuisance is that the debug assertions cannot flatten nested structures like polyvec for the bounds check, running into issue diffblue/cbmc#8570. We work around this by introducing a new `xxx_2d` (for 2-dimensional) macro which takes two dimensions and uses a two-step array access, circumventing the above CBMC issue. Signed-off-by: Hanno Becker <[email protected]>
@tautschnig @remi-delmas-3000 Any fix in sight? |
What behaviour were you expecting? In Michael's final example, k is uninitialized, so any reference to k looks like UB to me. Flattened out, x is pointing at 4 integers, so k could be anything in 0 .. 3. Let's try that:
then with CBMC 6.4.1
which doesn't look right to me, since we've only assumed that one element is < 42. What did I miss? |
@rod-chapman I don't follow, in 3 of 4 asserts, the |
When evaluating That said: I know which piece in the CBMC code base is causing us to go wrong (?) here, but I am wondering whether we are mis-optimising there or the input code is the culprit. |
Oh... so we're inferring that "(k is uninitialized && not (k != 2)) infers k == 2". |
If your code evaluated "k != 2 || ((int *)x)[k] < 42" at runtime with k uninitialized, then it's UB, because the left hand side of the (short circuit) || operator has to be evaluated first, right? |
@tautschnig I would assume the original example still fails with |
I don't understand what you mean -- |
In Michael's example:
k looks uninitialized to me... |
Ah, I was looking at your example. I think this isn't really related to the issue at hand, though -- you could add |
Disagree I think.
means that k is initialized to a legal value of type "unsigned", so evaluating its R-value is well-defined. "K uninitialized" is UB. Remember there are computers out there with scalar types where there are arbitrary bit patterns that are not a valid value of whatever type you think it should be. Boolean is the worst offender, because the number of bits stored (typically 8) exceeds the number of bits of information in the type (1). |
The following variant passes just fine, but note that typedef struct __attribute__((packed)) {
int data[2];
} arr;
typedef struct __attribute__((packed)) {
arr vec[2];
} arrvec;
int main() {
arrvec A;
arrvec *x = &A;
__CPROVER_assume(x->vec[1].data[0] < 42);
unsigned k;
__CPROVER_assert(k != 11 || ((char*)x)[k] < 42, "");
} |
@rod-chapman I'm not disagreeing that |
@tautschnig Thank you, this is interesting. We should look into the C standard, then, to figure out if the original example is valid to begin with. |
If a local variable like k is uninitialized, then you should reject the program before generating GOTO at all and just stop. |
Yes but this has nothing to do with the question at hand. |
Continuing the side conversation on uninitialised local variables: a check that enforces initialisation is coming, with #8545 |
To get back to the original question, the C standard does not guarantee the behaviour in the original example; this clearly relies on implementation-defined behaviour, and hence, may break depending on architecture and compiler. |
@kroening Thanks for chiming in! For my education, what exactly is platform/compiler-specific in the original example? Is the layout of the structure not fully specified by the C standard (except for the layout of |
I'm not sure. Is "packed" advisory or mandatory in C? (It's an "Advisory" pragma in Ada, so cannot actually be relied upon to do anything, so you should never use it!). I don't see any strictly "implementation defined" behaviour in Michael's example. That has a very specific meaning in the RM, which is not the same as "unspecified" or "undefined" (which are far worse.) |
The C standard does not say anything at all about packing structs (no, there is no "advisory" either). So yes, a compiler may well insert arbitrary padding between those fields. The C standard does guarantee that there is no padding within multi-dimensional arrays, however. This could be a standard-endorsed way to achieve what you are trying to do. |
The C standard doesn't provide for Patch forthcoming, just trying to make sure it doesn't break any existing tests. |
This specific issue cannot be about padding, though, because if padding was the issue, CBMC should reject it regardless of whether the access is through
(@tautschnig you want So it is still not clear to me what it is that causes CBMC to reject the |
Value-set based dereferencing must not take an access path through an object that precludes a subsequent index expression from accessing a different part of the object. Such a situation can arise when the value set has a known (constant) offset for the pointer that would identify one particular element in an array (within that object). The code using value-set based dereferencing, however, may be trying to resolve a subexpression of an index expression, where said index expression would lead to a different element that may itself be part of a different array within the same overall object. Fixes: diffblue#8570
Proposed fix to be found in #8578. |
CBMC won't catch cases where padding would have made a difference. |
Ok, then CBMC should not reject the original example. Thanks @tautschnig for fixing this already! |
Value-set based dereferencing must not take an access path through an object that precludes a subsequent index expression from accessing a different part of the object. Such a situation can arise when the value set has a known (constant) offset for the pointer that would identify one particular element in an array (within that object). The code using value-set based dereferencing, however, may be trying to resolve a subexpression of an index expression, where said index expression would lead to a different element that may itself be part of a different array within the same overall object. Fixes: diffblue#8570
Value-set based dereferencing must not take an access path through an object that precludes a subsequent index expression from accessing a different part of the object. Such a situation can arise when the value set has a known (constant) offset for the pointer that would identify one particular element in an array (within that object). The code using value-set based dereferencing, however, may be trying to resolve a subexpression of an index expression, where said index expression would lead to a different element that may itself be part of a different array within the same overall object. Fixes: diffblue#8570
Value-set based dereferencing must not take an access path through an object that precludes a subsequent index expression from accessing a different part of the object. Such a situation can arise when the value set has a known (constant) offset for the pointer that would identify one particular element in an array (within that object). The code using value-set based dereferencing, however, may be trying to resolve a subexpression of an index expression, where said index expression would lead to a different element that may itself be part of a different array within the same overall object. Fixes: diffblue#8570
I'm observing unexpected behaviour when trying to use
__CPROVER_forall
to access the fields of a nested array structure when cast as a flat array of cells. cc @tautschnig @remi-delmas-3000Minimal example:
The text was updated successfully, but these errors were encountered: