Skip to content
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

NULL-checking a pointer argument yields "Proof failed." #120

Closed
ttaubert opened this issue Mar 20, 2016 · 6 comments
Closed

NULL-checking a pointer argument yields "Proof failed." #120

ttaubert opened this issue Mar 20, 2016 · 6 comments

Comments

@ttaubert
Copy link

Assume a function that checks whether its pointer argument is NULL:

uint8_t test(uint8_t* a) {
    return a ? 0 : 1;
}

Now take a SAW script that proves it always returns 0 with a non-null argument:

llvm_verify m "test" [] do {
    llvm_ptr "a" (llvm_array 1 (llvm_int 8));
    a <- llvm_var "*a" (llvm_array 1 (llvm_int 8));
    llvm_return {{ 0:[8] }};
    llvm_verify_tactic abc;
};

This however returns "Proof failed.":

Loading module Cryptol
Loading file "test.saw"
When verifying @test:
Proof of return value failed.
Counterexample:
  lss__alloc0: 0
return value
Encountered:  1
Expected:     0
saw: user error (Proof failed.)

Adding llvm_assert_eq "*a" {{ [0:[8]] }}; to fix the input argument to a specific value doesn't help here either. The only way to make this work seems currently to remove the NULL-check.

@ttaubert
Copy link
Author

It seems that there's a related bug with:

bool test(uint8_t* a, uint8_t* b) {
    return a == b;
}

Although (I assume) the simulator allocates two arrays when verifying, the function always returns true.

@atomb atomb added the priority label Mar 30, 2016
@atomb atomb modified the milestone: 0.2-alpha Mar 30, 2016
@atomb atomb self-assigned this Mar 30, 2016
@atomb
Copy link
Contributor

atomb commented Mar 30, 2016

The issue going on here is that the return value from an allocation is currently just a free variable. There's no constraint that is isn't equal to some specific constant (say zero), or that it isn't equal to any other pointer. In the second example, it's not possible to prove that the function returns either true or false.

@atomb atomb removed this from the 0.2-alpha milestone Mar 30, 2016
@atomb
Copy link
Contributor

atomb commented May 24, 2016

This is partly fixed. We now assume that pointers declared in a spec passed to llvm_verify are non-null. We don't yet assume that they point to non-overlapping regions of memory, however, so the second example will still fail to prove.

Note that this is a lack of completeness rather than a lack of soundness. We can't exploit this issue to prove a program correct that is not correct. But we may not be able to prove a correct program to be correct.

@TomMD
Copy link
Contributor

TomMD commented Jun 14, 2017

Notice this issue appears in a different form with the crucible backend. I believe a faithful translation is:

load_crucible_llvm_module "test.bc";

crucible_llvm_verify "test" [] false
    do {
        aptr <- crucible_fresh_pointer (llvm_array 1 (llvm_int 8));
        a <- crucible_fresh_var "a" (llvm_array 1 (llvm_int 8));
        crucible_points_to aptr (crucible_term a);
        crucible_return (crucible_term {{ 0:[8]}});
        }
    do { abc; };

Which yields:

Loading module Cryptol
Loading file "/private/tmp/test.saw"
saw: user error ("crucible_llvm_verify" (/private/tmp/test.saw:3:1):
"crucible_points_to" (/private/tmp/test.saw:7:9):
typeOfSetupValue: Unresolved prestate variable:AllocIndex 0)

@brianhuffman
Copy link
Contributor

With some small changes, the example works: Change crucible_fresh_pointer to crucible_alloc, and add a crucible_execute_func statement.

crucible_llvm_verify "test" [] false
    do {
        aptr <- crucible_alloc (llvm_array 1 (llvm_int 8));
        a <- crucible_fresh_var "a" (llvm_array 1 (llvm_int 8));
        crucible_points_to aptr (crucible_term a);
        crucible_execute_func [aptr];
        crucible_return (crucible_term {{ 0:[8]}});
        }
    do { abc; };

We seriously need to improve the error messages, though. SAW should inform the user that crucible_points_to cannot be used with a crucible_fresh_pointer-derived pointer on the lhs. Also SAW should report when the crucible_execute_func statement is missing.

@atomb atomb added this to the 1.0 milestone Jun 4, 2019
@atomb atomb modified the milestones: 1.0, 0.4 Oct 1, 2019
@atomb atomb modified the milestones: 0.4, 1.0 Oct 21, 2019
@atomb atomb modified the milestones: 0.5, 0.6 Apr 27, 2020
@atomb atomb modified the milestones: 0.6, 0.7 Aug 21, 2020
@atomb atomb removed this from the 0.7 milestone Oct 16, 2020
brianhuffman pushed a commit that referenced this issue Apr 26, 2021
Check types in context of private symbols
@atomb
Copy link
Contributor

atomb commented Jul 22, 2021

The error messages are more informative now. Without the crucible_execute_func, we get:

Argument 0 unspecified when verifying "test"

And, before replacing crucible_fresh_pointer with crucible_alloc, we get:

[19:02:36.496] Loading file "/Users/atomb/galois/saw-script/misc/issue120/test.saw"
[19:02:36.581] Verifying test ...
[19:02:36.581] Simulating test ...
[19:02:36.583] Checking proof obligations test ...
[19:02:36.727] Subgoal failed: test safety assertion:
internal: error: in _SAW_verify_prestate
Memory store failed
Details:
  The region wasn't allocated, wasn't large enough, or was marked as readonly

[19:02:36.727] SolverStats {solverStatsSolvers = fromList ["SBV->Yices"], solverStatsGoalSize = 29}
[19:02:36.727] ----------Counterexample----------
[19:02:36.727] <<All settings of the symbolic variables constitute a counterexample>>
[19:02:36.727] ----------------------------------
[19:02:36.727] Stack trace:
"llvm_verify" (/Users/atomb/galois/saw-script/misc/issue120/test.saw:3:1-3:12):
Proof failed.

@atomb atomb closed this as completed Jul 22, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants