GaloisInc / saw-script

The SAW scripting language.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

mir_assert_ghost_value asserts equality of Cryptol objects without Eq instances

sauclovian-g opened this issue · comments

It seems like mir_assert_ghost_value should require Eq on the Cryptol values it's testing for equality. It doesn't seem to. Example follows:

First, make a trivial piece of code inc.rs so we can have something to do:

pub fn inc(x: u32) -> u32 {
   x + 1
}

Next create a file Object.cry that wraps a u32 in a newtype. Newtypes don't have instances, including Eq.

newtype Object = { val : [32] }

object_inc : Object -> Object
object_inc n = Object { val = n.val + 1 }

Now write this spec:

enable_experimental;
import "Object.cry";

m <- mir_load_module "inc.linked-mir.json";
g <- declare_ghost_state "g";

let inc_spec = do {
   // create a value for the ghost state and bind it
   gv <- mir_fresh_cryptol_var "gv" {| Object |};
   mir_ghost_value g gv;

   // create an argument
   x <- mir_fresh_var "x" mir_u32;
   mir_precond {{ x < 4096 }};

   // call the function with the argument
   mir_execute_func [mir_term x];

   // assert the ghost state hasn't changed
   mir_ghost_value g gv;
   // note that this will fail...
   //mir_ghost_value g {{ object_inc gv }};

   // return the updated value
   mir_return (mir_term {{ x + 1 }});
};

mir_verify m "inc::inc" [] false inc_spec z3;

Now run it:

% saw-rustc --target wasm32-unknown-unknown inc.rs
% saw spec.saw

Everything succeeds.

The ghost state is completely gratuitous and serves no purpose here, but it does get tested for equality (if you uncomment the second call to mir_ghost_value the verification fails) ... and yet there is no equality operation on Object and it shouldn't be allowed.

(What gets called for other less trivial cases without native equality, like Array?)

I expect this behavior is not limited to the mir version of the ghost variable handling, but I'm not set up to check easily.

The reason that works is that the second call to mir_ghost_value isn't using Cryptol's (==) function under the hood, but rather a SAWCore-specific notion of decidable equality defined here. Perhaps this is not the answer you were hoping for, since SAWCore has access to more equality-checking power than Cryptol's Eq class does, but that is the state of things.

(What gets called for other less trivial cases without native equality, like Array?)

This calls out to arrayEq, which is SAWCore's equivalent of the Cryptol function of the same name.

Hmm. I think that's capable of causing problems. At least if it's implemented the way it looks like (equality of SMT-level representation). Are Cryptol Rational values always normalized by the time they get there? Are there other things where the representation of a given value isn't unique? (The common cases where this comes up in Coq, which are integers mod N and finite maps, don't really apply.)

Also in the context of #2040 I think it means that mir_assert (mir_get_ghost_value g == k) and mir_assert_ghost_value g k aren't the same as the former is going to end up using Cryptol Eq.

however, it doesn't seem like a priority concern...

Are Cryptol Rational values always normalized by the time they get there?

I don't believe so. Note that SAW doesn't really support Cryptol rational values, however.

Are there other things where the representation of a given value isn't unique?

Higher-order function values come to mind. However, those aren't supported by SAWCore's decidable equality (and therefore can't be equated via mir_ghost_value), so they aren't really that relevant to this discussion.

Also in the context of #2040 I think it means that mir_assert (mir_get_ghost_value g == k) and mir_assert_ghost_value g k aren't the same as the former is going to end up using Cryptol Eq.

Right, this means that depending on the type of k, you may need to use something like arrayEq instead of (==). Regardless of whatever operation you need to use, however, it should desugar into a SAWCore function that does basically the same thing as what SAWCore's decidable equality performs.

Hmm, here's another case where the representation isn't unique: when the value is a pointer to something else. Strings are the obvious case. So given for example something like newtype String = { index: [12] } and then an array of 4096 lists of [8], you'd need to take precautions to make sure no string appears in the table more than once. Otherwise you end up using pointer equality instead of value equality.

(Why would one do that? Well, we're about to in pursuit of modeling an implementation that works this way. We don't get to choose whether it hash-conses its strings and I'm pretty sure it does not...)

But I think that's an argument in favor of mir_get_ghost_value, not necessarily one in favor of requiring Eq instances on ghost state types. I'm inclined to close this issue rather than leave it in the backlog forever...