Skip to content

Commit a59b533

Browse files
authored
Merge pull request #2670 from GaloisInc/issue_2657
Add support for using size polymorphic Cryptol functions in crux-mir-comp
2 parents fe6a62f + 6f2ad0d commit a59b533

32 files changed

+683
-65
lines changed

crucible-mir-comp/src/Mir/Compositional/Convert.hs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import qualified Data.Set as Set
3030
import Data.Vector (Vector)
3131
import qualified Data.Vector as V
3232
import GHC.Stack (HasCallStack)
33+
import qualified Data.BitVector.Sized as BV
3334

3435
import Lang.Crucible.Backend
3536
import Lang.Crucible.Simulator.RegValue
@@ -52,6 +53,7 @@ import SAWCentral.Crucible.MIR.TypeShape
5253
import Mir.Intrinsics
5354
import qualified Mir.Mir as M
5455
import Mir.Compositional.State
56+
import Lang.Crucible.Simulator
5557

5658

5759
-- | Run `f` on each `SymExpr` in `v`.
@@ -306,6 +308,85 @@ exprToTerm sym sc w4VarMapRef val = liftIO $ do
306308
term <- SAW.scVariable sc ec
307309
return term
308310

311+
312+
-- | Try to convert a Crucible register value into a SAW core `Term`, using
313+
-- a `CryTermAdaptor` to validate and convert references to slices.
314+
-- We check that references to slices match the expected length specified
315+
-- in the `CryTermAdaptor`, and if so we read out the value stored in
316+
-- in the slice.
317+
regToTermWithAdapt :: forall m p sym t fs tp0 rtp args ret.
318+
( IsSymInterface sym,
319+
sym ~ MirSym t fs,
320+
m ~ OverrideSim (p sym) sym MIR rtp args ret) =>
321+
sym ->
322+
SAW.SharedContext ->
323+
String ->
324+
IORef (Map SAW.VarIndex (Some (W4.Expr t))) ->
325+
CryTermAdaptor Integer ->
326+
TypeShape tp0 ->
327+
RegValue sym tp0 ->
328+
m SAW.Term
329+
regToTermWithAdapt sym sc name w4VarMapRef ada0 shp0 rv0 = go ada0 shp0 rv0
330+
where
331+
go :: forall tp.
332+
CryTermAdaptor Integer ->
333+
TypeShape tp ->
334+
RegValue sym tp ->
335+
m SAW.Term
336+
go ada shp rv =
337+
case (ada, shp, rv) of
338+
(NoAdapt, _, _) -> regToTerm sym sc name w4VarMapRef shp rv
339+
(AdaptTuple as, TupleShape _ elems, ag) -> do
340+
terms <- accessMirAggregate' sym elems as ag $ \_off _sz shp' rv' a -> go a shp' rv'
341+
liftIO $ SAW.scTuple sc terms
342+
(AdaptArray a, ArrayShape _ _ shp', vec) -> do
343+
terms <- goVector a shp' vec
344+
tyTerm <- shapeToTerm' sc a shp'
345+
liftIO $ SAW.scVector sc tyTerm terms
346+
(AdaptDerefSlice col n elAda, SliceShape _ty elT M.Immut tpr, Ctx.Empty Ctx.:> RV mirPtr Ctx.:> RV lenExpr) ->
347+
case BV.asUnsigned <$> W4.asBV lenExpr of
348+
Nothing ->
349+
fail "Slice length is not statically known"
350+
351+
Just n1
352+
| n /= n1 ->
353+
fail (unlines [
354+
"Slice length mismatch:",
355+
" Expected: " ++ show n,
356+
" Actual : " ++ show n1
357+
])
358+
| otherwise ->
359+
do
360+
let elShp = tyToShapeEq col elT tpr
361+
vals <-
362+
forM [ 0 .. n - 1 ] $ \i ->
363+
do
364+
iExpr <- liftIO (W4.bvLit sym knownNat (BV.mkBV knownNat i))
365+
elemPtr <- mirRef_offsetWrapSim mirPtr iExpr
366+
r <- readMirRefSim tpr elemPtr
367+
go elAda elShp r
368+
elTyTerm <- shapeToTerm' sc elAda elShp
369+
liftIO (SAW.scVector sc elTyTerm vals)
370+
371+
372+
373+
_ -> fail $
374+
"type error: " ++ name ++ " got argument of unsupported type " ++ show (shapeType shp)
375+
376+
goVector :: forall tp.
377+
CryTermAdaptor Integer ->
378+
TypeShape tp ->
379+
MirVector sym tp ->
380+
m [SAW.Term]
381+
goVector ada shp (MirVector_Vector v) = mapM (go ada shp) $ toList v
382+
goVector ada shp (MirVector_PartialVector pv) = do
383+
forM (toList pv) $ \rv -> do
384+
let rv' = readMaybeType sym "field" (shapeType shp) rv
385+
go ada shp rv'
386+
goVector _ada _shp (MirVector_Array _) = fail $
387+
"regToTerm: MirVector_Array not supported"
388+
389+
309390
regToTerm :: forall sym t fs tp0 m.
310391
(IsSymInterface sym, sym ~ MirSym t fs, MonadIO m, MonadFail m) =>
311392
sym ->

crux-mir-comp/DESIGN.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,74 @@ implementations of some cryptographic algorithms) are usually declared as
147147
immutable and thus always retain their initial values.
148148

149149

150+
## Cryptol
151+
152+
It is possible to import Cryptol expressions into symbolic Rust test cases to
153+
help write specifications (e.g., if you want to check that a Rust implementation
154+
matches a Cryptol specification). This is done by using the `cryptol!` macro
155+
defined in the `crucible` crate. Here's an example illustrating how to
156+
import a Cryptol function `myCryFun` defined in Cryptol module `SomeCryMod`,
157+
and bind it as the Rust function `myRustFun`:
158+
159+
```Cryptol
160+
module SomeCryMod where
161+
f: [8] -> [32]
162+
f x = 0 # x
163+
```
164+
```Rust
165+
use crucible::*;
166+
cryptol! {
167+
path "SomeCryMod";
168+
pub fn myRustFun(x: u8) -> u32 = r"myCryFun";
169+
}
170+
```
171+
172+
Тhe `path` component of the macro specifies a Cryptol module,
173+
the string after the `=` is a Cryptol expression, and the rest of the
174+
declaration specifies how to invoke evaluating the Cryptol expression from
175+
Rust. If the declaration contains `const` generics, then the Cryptol expression
176+
is specified as a format string which may refer to the values of the const
177+
generic parameters in curly braces (literal curly braces need to be escaped
178+
as a double curly brace). Here's an example of how to use a function with
179+
const generics:
180+
```Cryptol``
181+
module Cryptol where
182+
sum : {n, a} (fin n, Eq a, Ring a) => [n]a -> a
183+
sum = foldl (+) zero
184+
```
185+
```Rust
186+
extern crate crucible;
187+
use crucible::*;
188+
cryptol! {
189+
path "Cryptol";
190+
pub fn f<const N: usize>(x: &[u8]) -> u8 = r"sum`{{{N},[8]}}";
191+
}
192+
```
193+
194+
It is important that the type of the Cryptol expression is compatible
195+
with the type of the declared Rust function, according to the following
196+
rules:
197+
198+
* The Cryptol type may have only numeric type parameters
199+
* The first parameters of the Rust function should correspond to the
200+
numeric type parameters of the Cryptol expression; they should be
201+
of basic Rust numeric types (e.g., `usize`)
202+
* Symbolic expressions may *not* be used as arguments corresponding to
203+
numeric type parameters.
204+
* The remaining parameters to the Rust function should correspond to the
205+
parameters in the Cryptol type as follows:
206+
* Cryptol's `Bit` is represented as Rust's `bool`
207+
* A Cryptol sequence of length `n` may be represented as a Rust
208+
array of length `n`.
209+
* In the parameters of the Rust function, Cryptol sequences may also
210+
be represented as reference to slices. The length of the slice should
211+
match the length of the Cryptol sequence, which is checked dynamically.
212+
* Cryptol `Bit` sequences of lengths corresponding to Rust's basic
213+
numeric types may also be represented with those types
214+
(e.g., `[8]` may be represented with `u8` or `i8`).
215+
* Cryptol tuples are represented as Rust tuples.
216+
217+
150218
## Current limitations
151219

152220
* Only one specification can be provided at a time for each function. There is

crux-mir-comp/crux-mir-comp.cabal

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ library
4040
parameterized-utils >= 1.0.8,
4141
what4,
4242

43+
-- galois packages from hackage
44+
bv-sized,
45+
4346
-- internal subpackages/sublibraries in the saw tree
4447
crucible-mir-comp,
4548
saw:saw-core,

0 commit comments

Comments
 (0)