packages feed

crucible 0.7.2 → 0.9

raw patch · 23 files changed

Files

CHANGELOG.md view
@@ -1,3 +1,22 @@+# 0.9 -- 2026-01-29++# 0.8.0 -- 2025-11-09++* Add `setExecResultContext`, `setExecStateContext`+* Add `Lang.Crucible.Simulator.RecordAndReplay`, a module with two new execution+  features for recording and replaying control-flow traces.+* Add a `GlobalPair` argument to `AbortedExit`.+* Add new helpers for extracting `SymGlobalState`s: `exec{Result,State}Globals`.+* Add `typedOverride` for constructing `TypedOverride`s with statically-known+  signatures.+* Add `bindTypedOverride` for binding `TypedOverride`s to `FnHandle`s.+* Add `FunctorF`, `FoldableF`, and `TraversableF` instances for `CrucibleEvent`,+  `CrucibleAssumption`, and `CrucibleAssumptions`.+* Add `gcAddTopLevelAssume`, for making top-level assumptions.+* Rename functions in `Lang.Crucible.Concretize` to match What4's conventions.+  In particular, rename  `concRegValue` to `groundRegValue`, `concRegEntry` to+  `groundRegEntry`, and `concRegMap` to `groundRegMap`.+ # 0.7.2 -- 2025-03-21  * Add support for Bitwuzla as an online SMT solver backend.
crucible.cabal view
@@ -1,6 +1,6 @@ Cabal-version: 2.2 Name:          crucible-Version:       0.7.2+Version:       0.9 Author:        Galois Inc. Maintainer:    rscott@galois.com, kquick@galois.com, langston@galois.com Copyright:     (c) Galois, Inc 2014-2022@@ -14,6 +14,9 @@   (SSA) form control flow graphs, and a symbolic simulation engine for executing   programs expressed in this format.  It also provides support for communicating with   a variety of SAT and SMT solvers, including Z3, CVC4, Yices, STP, and dReal.+  .+  For an overview of Crucible please have a look at "Lang.Crucible.README"+   extra-doc-files: CHANGELOG.md  source-repository head@@ -45,7 +48,7 @@   import: bldflags   build-depends:     async,-    base >= 4.13 && < 4.20,+    base >= 4.13 && < 4.21,     bimap,     bv-sized >= 1.0.0 && < 1.1,     containers >= 0.5.9.0,@@ -101,6 +104,7 @@     Lang.Crucible.CFG.SSAConversion     Lang.Crucible.CFG.EarlyMergeLoops     Lang.Crucible.FunctionHandle+    Lang.Crucible.README     Lang.Crucible.Simulator     Lang.Crucible.Simulator.Breakpoint     Lang.Crucible.Simulator.BoundedExec@@ -117,6 +121,7 @@     Lang.Crucible.Simulator.PathSplitting     Lang.Crucible.Simulator.PositionTracking     Lang.Crucible.Simulator.Profiling+    Lang.Crucible.Simulator.RecordAndReplay     Lang.Crucible.Simulator.RegMap     Lang.Crucible.Simulator.RegValue     Lang.Crucible.Simulator.SimError@@ -163,15 +168,18 @@   import: bldflags   type: exitcode-stdio-1.0   hs-source-dirs: test/helpers---  other-modules:+  other-modules:+    SymSequence   main-is: Main.hs   build-depends: base,                  hspec >= 2.5,                  crucible,+                 hedgehog,                  lens,                  panic >= 0.3,                  parameterized-utils,                  tasty >= 0.10,                  tasty-hspec >= 1.1,+                 tasty-hedgehog >= 1.2,                  tasty-hunit,                  what4
src/Lang/Crucible/Backend.hs view
@@ -4,8 +4,8 @@ License     : BSD3 Maintainer  : Joe Hendrix <jhendrix@galois.com> -This module provides an interface that symbolic backends must provide-for interacting with the symbolic simulator.+This module provides the interface that the symbolic simulator uses when+interacting with symbolic backends (i.e., SMT solvers).  Compared to the solver connections provided by What4, Crucible backends provide a facility for managing an /assumption stack/ (see 'AS.AssumptionStack').  Note@@ -81,6 +81,7 @@   , ppProofObligation   , backendOptions   , assertThenAssumeConfigOption+  , ppAssumptionState   ) where  import           Control.Exception(Exception(..), throwIO)@@ -110,6 +111,7 @@ import qualified Lang.Crucible.Backend.AssumptionStack as AS import qualified Lang.Crucible.Backend.ProofGoals as PG import           Lang.Crucible.Simulator.SimError+import Lang.Crucible.Backend.ProofGoals (ppGoalCollector)  type Assertion sym = LabeledPred (Pred sym) SimError type ProofObligation sym = AS.ProofGoal (Assumptions sym) (Assertion sym)@@ -311,6 +313,11 @@   resetAssumptionState :: bak -> IO ()   resetAssumptionState bak = restoreAssumptionState bak PG.emptyGoalCollector +  -- | Get the state of the backend+  --+  -- In contrast to 'saveAssumptionState', this also includes the goals.+  getBackendState :: bak -> IO (AssumptionState sym)+ assertThenAssumeConfigOption :: ConfigOption BaseBoolType assertThenAssumeConfigOption = configOption knownRepr "assertThenAssume" @@ -490,3 +497,21 @@  ppGl =    PP.indent 2 $    PP.vsep [ppSimError (gl^.labeledPredMsg), printSymExpr (gl^.labeledPred)]++-- | Pretty-printer for 'AssumptionState'.+ppAssumptionState ::+  IsExpr (SymExpr sym) =>+  proxy sym ->+  AssumptionState sym ->+  PP.Doc ann+ppAssumptionState _proxy = ppGoalCollector ppAssumptions ppPred+  where+  ppPred (LabeledPred p simErr) =+    PP.vcat+    [ "Labeled predicate:"+    , PP.indent 2 $+        PP.vcat+        [ printSymExpr p+        , ppSimError simErr+        ]+    ]
src/Lang/Crucible/Backend/AssumptionStack.hs view
@@ -4,17 +4,16 @@ License     : BSD3 Maintainer  : Rob Dockins <rdockins@galois.com> -This module provides management support for keeping track-of a context of logical assumptions.  The API provided here-is similar to the interactive mode of an SMT solver.  Logical-conditions can be assumed into the current context, and bundles-of assumptions are organized into frames which are pushed and-popped by the user to manage the state.+This module provides management support for keeping track of a context of+logical assumptions and proof obligations that arise from symbolic simulation.+The API provided here is similar to the interactive mode of an SMT solver.+Logical conditions can be assumed into the current context, and conjunctions+of assumptions are organized into frames which are pushed and popped by the+simulator to manage the state. -Additionally, proof goals can be asserted to the system.  These will be-turned into complete logical statements by assuming the current context-and be stashed in a collection of remembered goals for later dispatch to-solvers.+Additionally, proof goals can be asserted to the system. These will be turned+into complete logical statements by assuming the current context and will be+stashed in a collection of remembered goals for later dispatch to SMT solvers. -} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-}@@ -70,11 +69,13 @@   , assumeFrameCond  :: asmp   } --- | An assumption stack is a data structure for tracking---   logical assumptions and proof obligations.  Assumptions---   can be added to the current stack frame, and stack frames---   may be pushed (to remember a previous state) or popped---   to restore a previous state.+-- | An assumption stack is a data structure for tracking logical assumptions+--   and proof obligations that arise from symbolic simulation.  Assumptions+--   can be added to the current stack frame, and stack frames may be pushed (to+--   remember a previous state) or popped (to restore a previous state).+--+--   The main use of 'AssumptionStack' is as the state of the simple or online+--   backends. data AssumptionStack asmp ast =   AssumptionStack   { assumeStackGen   :: IO FrameIdentifier
src/Lang/Crucible/Backend/Assumptions.hs view
@@ -41,13 +41,17 @@   , assumptionsPred   , flattenAssumptions   , assumptionsTopLevelLocs+  , ppAssumptions'+  , ppAssumptions   ) where   import           Control.Lens (Traversal, folded) import           Data.Kind (Type)+import qualified Data.Foldable as F import           Data.Functor.Identity import           Data.Functor.Const+import qualified Data.Parameterized.TraversableF as TF import qualified Data.Sequence as Seq import           Data.Sequence (Seq) import qualified Prettyprinter as PP@@ -76,6 +80,13 @@     -- ^ An assumption justified by a proof of the impossibility of     -- a certain simulator error. +instance TF.FunctorF CrucibleAssumption where+  fmapF = TF.fmapFDefault+instance TF.FoldableF CrucibleAssumption where+  foldMapF = TF.foldMapFDefault+instance TF.TraversableF CrucibleAssumption where+  traverseF = traverseAssumption+ -- | This type describes events we can track during program execution. data CrucibleEvent (e :: BaseType -> Type) where   -- | This event describes the creation of a symbolic variable.@@ -91,13 +102,24 @@     ProgramLoc ->     CrucibleEvent e +instance TF.FunctorF CrucibleEvent where+  fmapF = TF.fmapFDefault+instance TF.FoldableF CrucibleEvent where+  foldMapF = TF.foldMapFDefault+instance TF.TraversableF CrucibleEvent where+  traverseF = traverseEvent+ -- | Pretty print an event-ppEvent :: IsExpr e => CrucibleEvent e -> PP.Doc ann-ppEvent (CreateVariableEvent loc nm _tpr v) =-  "create var" PP.<+> PP.pretty nm PP.<+> "=" PP.<+> printSymExpr v PP.<+> "at" PP.<+> PP.pretty (plSourceLoc loc)-ppEvent (LocationReachedEvent loc) =+ppEvent' :: (forall t. e t -> PP.Doc ann) -> CrucibleEvent e -> PP.Doc ann+ppEvent' ppExp (CreateVariableEvent loc nm _tpr v) =+  "create var" PP.<+> PP.pretty nm PP.<+> "=" PP.<+> ppExp v PP.<+> "at" PP.<+> PP.pretty (plSourceLoc loc)+ppEvent' _ppExp (LocationReachedEvent loc) =   "reached" PP.<+> PP.pretty (plSourceLoc loc) PP.<+> "in" PP.<+> PP.pretty (plFunction loc) +-- | Pretty print an event+ppEvent :: IsExpr e => CrucibleEvent e -> PP.Doc ann+ppEvent = ppEvent' printSymExpr+ -- | Return the program location associated with an event eventLoc :: CrucibleEvent e -> ProgramLoc eventLoc (CreateVariableEvent loc _ _ _) = loc@@ -152,6 +174,21 @@ instance Monoid (CrucibleAssumptions e) where   mempty = ManyAssumptions mempty +instance TF.FunctorF CrucibleAssumptions where+  fmapF = TF.fmapFDefault+instance TF.FoldableF CrucibleAssumptions where+  foldMapF = TF.foldMapFDefault+instance TF.TraversableF CrucibleAssumptions where+  traverseF f = \case+    SingleAssumption a ->+      SingleAssumption <$> TF.traverseF f a+    SingleEvent e ->+      SingleEvent <$> TF.traverseF f e+    ManyAssumptions xs ->+      ManyAssumptions <$> traverse (TF.traverseF f) xs+    MergeAssumptions c xs ys ->+      MergeAssumptions <$> f c <*> TF.traverseF f xs <*> TF.traverseF f ys+ singleAssumption :: CrucibleAssumption e -> CrucibleAssumptions e singleAssumption x = SingleAssumption x @@ -239,6 +276,8 @@  ppAssumption :: (forall tp. e tp -> PP.Doc ann) -> CrucibleAssumption e -> PP.Doc ann ppAssumption ppDoc e =+  -- TODO(lb): These should really all be `align`ed, but that breaks a bunch+  -- of tests.   case e of     GenericAssumption l msg p ->       PP.vsep [ ppLocated l (PP.pretty msg)@@ -266,3 +305,27 @@      ppLoc :: ProgramLoc -> PP.Doc ann     ppLoc l = PP.pretty (plSourceLoc l)++-- | Pretty-print 'CrucibleAssumptions'.+ppAssumptions' ::+  -- | How to print expressions. If @'IsExpr' e@ holds, then see 'ppAssumptions'+  -- for a version that uses 'printSymExpr'.+  (forall tp. e tp -> PP.Doc ann) ->+  CrucibleAssumptions e ->+  PP.Doc ann+ppAssumptions' ppExp =+  \case+    SingleAssumption asmp -> ppAssumption ppExp asmp+    SingleEvent e -> ppEvent' ppExp e+    ManyAssumptions asmps -> PP.list (map (ppAssumptions' ppExp) (F.toList asmps))+    MergeAssumptions b thn els ->+      PP.align $+        PP.vcat+        [ "if " <> PP.align (ppExp b)+        , "then " <> PP.align (ppAssumptions' ppExp thn)+        , "else " <> PP.align (ppAssumptions' ppExp els)+        ]++-- | @'ppAssumptions' = `ppAssumptions'` 'printSymExpr'@+ppAssumptions :: IsExpr e => CrucibleAssumptions e -> PP.Doc ann+ppAssumptions = ppAssumptions' printSymExpr
src/Lang/Crucible/Backend/Goals.hs view
@@ -7,12 +7,14 @@ proof obligations, and the current state of assumptions. -} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TupleSections #-}  module Lang.Crucible.Backend.Goals   ( ProofGoal(..)   , Goals(..)+  , ppGoals   , goalsToList   , assuming   , proveAll@@ -29,6 +31,7 @@ import           Data.Functor.Const (Const(..)) import           Data.Sequence (Seq) import qualified Data.Sequence as Seq+import qualified Prettyprinter as PP  -- | A proof goal consists of a collection of assumptions --   that were in scope when an assertion was made, together@@ -52,6 +55,35 @@     -- | A conjunction of two goals.   | ProveConj !(Goals asmp goal) !(Goals asmp goal)     deriving Show++-- | Intended for debugging, this is not generally a user-facing datatype.+ppGoals ::+  (asmp -> PP.Doc ann) ->+  (goal -> PP.Doc ann) ->+  Goals asmp goal ->+  PP.Doc ann+ppGoals ppAsmp ppGoal =+  \case+    Assuming asmp gls ->+      PP.align $+        PP.vcat+        [ PP.pretty "Assuming:"+        , PP.indent 2 (ppAsmp asmp)+        , PP.pretty "Prove:"+        , PP.indent 2 (ppGoals ppAsmp ppGoal gls)+        ]+    Prove gl -> ppGoal gl+    ProveConj gls gls' ->+      PP.align $+        PP.vcat+        [ PP.pretty "Prove both:"+        , PP.indent 2 (ppGoals ppAsmp ppGoal gls)+        , PP.indent 2 (ppGoals ppAsmp ppGoal gls')+        ]++-- | Intended for debugging, this is not generally a user-facing datatype.+instance (PP.Pretty asmp, PP.Pretty goal) => PP.Pretty (Goals asmp goal) where+  pretty = ppGoals PP.pretty PP.pretty  -- | Construct a goal that first assumes a collection of --   assumptions and then states a goal.
src/Lang/Crucible/Backend/Online.hs view
@@ -4,19 +4,26 @@ -- Description : A solver backend that maintains a persistent connection -- Copyright   : (c) Galois, Inc 2015-2016 -- License     : BSD3--- Maintainer  : Joe Hendrix <jhendrix@galois.com>+-- Maintainer  : Ryan Scott <rscott@galois.com>, Langston Barrett <langston@galois.com> -- Stability   : provisional ----- The online backend maintains an open connection to an SMT solver--- that is used to prune unsatisfiable execution traces during simulation.--- At every symbolic branch point, the SMT solver is queried to determine--- if one or both symbolic branches are unsatisfiable.--- Only branches with satisfiable branch conditions are explored.+-- A solver backend ('IsSymBackend') that maintains an open connection to an+-- SMT solver (in contrast to "Lang.Crucible.Backend.Simple"). ----- The online backend also allows override definitions access to a--- persistent SMT solver connection.  This can be useful for some--- kinds of algorithms that benefit from quickly performing many--- small solver queries in a tight interaction loop.+-- The primary intended use-case is to prune unsatisfiable execution+-- traces during simulation using the execution feature provided by+-- "Lang.Crucible.Simulator.PathSatisfiability". That execution feature is+-- parameterized over a function argument that can be instantiated with this+-- module's 'considerSatisfiability'.+--+-- The online backend also allows override definitions access to a persistent+-- SMT solver connection. This can be useful for some kinds of algorithms+-- that benefit from quickly performing many small solver queries in a tight+-- interaction loop.+--+-- The online backend is not currently used to dispatch proof obligations during+-- symbolic execution, see [GaloisInc/crucible#369, \"Interleave proof with+-- simulation\"](https://github.com/GaloisInc/crucible/issues/369). ------------------------------------------------------------------------  {-# LANGUAGE DeriveDataTypeable #-}@@ -27,9 +34,14 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-}+ module Lang.Crucible.Backend.Online-  ( -- * OnlineBackend-    OnlineBackend+  ( -- * Configuration options+    solverInteractionFile+  , enableOnlineBackend+  , onlineBackendOptions+    -- * OnlineBackend+  , OnlineBackend   , withOnlineBackend   , newOnlineBackend   , checkSatisfiable@@ -39,13 +51,10 @@   , restoreSolverState   , UnsatFeatures(..)   , unsatFeaturesToProblemFeatures-    -- ** Configuration options-  , solverInteractionFile-  , enableOnlineBackend-  , onlineBackendOptions-    -- ** Branch satisfiability+    -- * Branch satisfiability   , BranchResult(..)   , considerSatisfiability+    -- * Backends for different solvers     -- ** Yices   , YicesOnlineBackend   , withYicesOnlineBackend@@ -69,7 +78,6 @@   , withSTPOnlineBackend   ) where - import           Control.Lens ( (^.) ) import           Control.Monad import           Control.Monad.Fix (mfix)@@ -104,10 +112,13 @@ import qualified What4.Solver.Z3 as Z3  import           Lang.Crucible.Backend-import           Lang.Crucible.Backend.AssumptionStack as AS+import qualified Lang.Crucible.Backend.AssumptionStack as AS import qualified Lang.Crucible.Backend.ProofGoals as PG import           Lang.Crucible.Simulator.SimError +--------------------------------------------------------------------------------+-- Configuration options+ data UnsatFeatures   = NoUnsatFeatures      -- ^ Do not compute unsat cores or assumptions@@ -181,7 +192,7 @@   ProblemFeatures ->   IO (OnlineBackend solver scope st fs) newOnlineBackend sym feats =-  do stk <- initAssumptionStack (sym ^. B.exprCounter)+  do stk <- AS.initAssumptionStack (sym ^. B.exprCounter)      procref <- newIORef SolverNotStarted      featref <- newIORef feats @@ -224,176 +235,6 @@     )  -type YicesOnlineBackend scope st fs = OnlineBackend Yices.Connection scope st fs---- | Do something with a Yices online backend.---   The backend is only valid in the continuation.------   The Yices configuration options will be automatically---   installed into the backend configuration object.------   n.b. the explicit forall allows the fs to be expressed as the---   first argument so that it can be dictated easily from the caller.---   Example:------   > withYicesOnlineBackend FloatRealRepr ng f'-withYicesOnlineBackend ::-  (MonadIO m, MonadMask m) =>-  B.ExprBuilder scope st fs ->-  UnsatFeatures ->-  ProblemFeatures ->-  (YicesOnlineBackend scope st fs -> m a) ->-  m a-withYicesOnlineBackend sym unsatFeat extraFeatures action =-  let feat = Yices.yicesDefaultFeatures .|. unsatFeaturesToProblemFeatures unsatFeat  .|. extraFeatures in-  withOnlineBackend sym feat $ \bak ->-    do liftIO $ tryExtendConfig Yices.yicesOptions (getConfiguration sym)-       action bak--type Z3OnlineBackend scope st fs = OnlineBackend (SMT2.Writer Z3.Z3) scope st fs---- | Do something with a Z3 online backend.---   The backend is only valid in the continuation.------   The Z3 configuration options will be automatically---   installed into the backend configuration object.------   n.b. the explicit forall allows the fs to be expressed as the---   first argument so that it can be dictated easily from the caller.---   Example:------   > withz3OnlineBackend FloatRealRepr ng f'-withZ3OnlineBackend ::-  (MonadIO m, MonadMask m) =>-  B.ExprBuilder scope st fs ->-  UnsatFeatures ->-  ProblemFeatures ->-  (Z3OnlineBackend scope st fs -> m a) ->-  m a-withZ3OnlineBackend sym unsatFeat extraFeatures action =-  let feat = (SMT2.defaultFeatures Z3.Z3 .|. unsatFeaturesToProblemFeatures unsatFeat .|. extraFeatures) in-  withOnlineBackend sym feat $ \bak ->-    do liftIO $ tryExtendConfig Z3.z3Options (getConfiguration sym)-       action bak--type BitwuzlaOnlineBackend scope st fs = OnlineBackend (SMT2.Writer Bitwuzla.Bitwuzla) scope st fs---- | Do something with a Bitwuzla online backend.---   The backend is only valid in the continuation.------   The Bitwuzla configuration options will be automatically---   installed into the backend configuration object.------   > withBitwuzlaOnineBackend FloatRealRepr ng f'-withBitwuzlaOnlineBackend ::-  (MonadIO m, MonadMask m) =>-  B.ExprBuilder scope st fs ->-  UnsatFeatures ->-  ProblemFeatures ->-  (BitwuzlaOnlineBackend scope st fs -> m a) ->-  m a-withBitwuzlaOnlineBackend sym unsatFeat extraFeatures action =-  let feat = (SMT2.defaultFeatures Bitwuzla.Bitwuzla .|. unsatFeaturesToProblemFeatures unsatFeat .|. extraFeatures) in-  withOnlineBackend sym feat $ \bak -> do-    liftIO $ tryExtendConfig Bitwuzla.bitwuzlaOptions (getConfiguration sym)-    action bak--type BoolectorOnlineBackend scope st fs = OnlineBackend (SMT2.Writer Boolector.Boolector) scope st fs---- | Do something with a Boolector online backend.---   The backend is only valid in the continuation.------   The Boolector configuration options will be automatically---   installed into the backend configuration object.------   > withBoolectorOnineBackend FloatRealRepr ng f'-withBoolectorOnlineBackend ::-  (MonadIO m, MonadMask m) =>-  B.ExprBuilder scope st fs ->-  UnsatFeatures ->-  (BoolectorOnlineBackend scope st fs -> m a) ->-  m a-withBoolectorOnlineBackend sym unsatFeat action =-  let feat = (SMT2.defaultFeatures Boolector.Boolector .|. unsatFeaturesToProblemFeatures unsatFeat) in-  withOnlineBackend sym feat $ \bak -> do-    liftIO $ tryExtendConfig Boolector.boolectorOptions (getConfiguration sym)-    action bak--type CVC4OnlineBackend scope st fs = OnlineBackend (SMT2.Writer CVC4.CVC4) scope st fs---- | Do something with a CVC4 online backend.---   The backend is only valid in the continuation.------   The CVC4 configuration options will be automatically---   installed into the backend configuration object.------   n.b. the explicit forall allows the fs to be expressed as the---   first argument so that it can be dictated easily from the caller.---   Example:------   > withCVC4OnlineBackend FloatRealRepr ng f'-withCVC4OnlineBackend ::-  (MonadIO m, MonadMask m) =>-  B.ExprBuilder scope st fs ->-  UnsatFeatures ->-  ProblemFeatures ->-  (CVC4OnlineBackend scope st fs -> m a) ->-  m a-withCVC4OnlineBackend sym unsatFeat extraFeatures action =-  let feat = (SMT2.defaultFeatures CVC4.CVC4 .|. unsatFeaturesToProblemFeatures unsatFeat .|. extraFeatures) in-  withOnlineBackend sym feat $ \bak -> do-    liftIO $ tryExtendConfig CVC4.cvc4Options (getConfiguration sym)-    action bak--type CVC5OnlineBackend scope st fs = OnlineBackend (SMT2.Writer CVC5.CVC5) scope st fs---- | Do something with a CVC5 online backend.---   The backend is only valid in the continuation.------   The CVC5 configuration options will be automatically---   installed into the backend configuration object.------   n.b. the explicit forall allows the fs to be expressed as the---   first argument so that it can be dictated easily from the caller.---   Example:------   > withCVC5OnlineBackend FloatRealRepr ng f'-withCVC5OnlineBackend ::-  (MonadIO m, MonadMask m) =>-  B.ExprBuilder scope st fs ->-  UnsatFeatures ->-  ProblemFeatures ->-  (CVC5OnlineBackend scope st fs -> m a) ->-  m a-withCVC5OnlineBackend sym unsatFeat extraFeatures action =-  let feat = (SMT2.defaultFeatures CVC5.CVC5 .|. unsatFeaturesToProblemFeatures unsatFeat .|. extraFeatures) in-  withOnlineBackend sym feat $ \bak -> do-    liftIO $ tryExtendConfig CVC5.cvc5Options (getConfiguration sym)-    action bak--type STPOnlineBackend scope st fs = OnlineBackend (SMT2.Writer STP.STP) scope st fs---- | Do something with a STP online backend.---   The backend is only valid in the continuation.------   The STO configuration options will be automatically---   installed into the backend configuration object.------   n.b. the explicit forall allows the fs to be expressed as the---   first argument so that it can be dictated easily from the caller.---   Example:------   > withSTPOnlineBackend FloatRealRepr ng f'-withSTPOnlineBackend ::-  (MonadIO m, MonadMask m) =>-  B.ExprBuilder scope st fs ->-  (STPOnlineBackend scope st fs -> m a) ->-  m a-withSTPOnlineBackend sym action =-  withOnlineBackend sym (SMT2.defaultFeatures STP.STP) $ \bak -> do-    liftIO $ tryExtendConfig STP.stpOptions (getConfiguration sym)-    action bak- -- | Shutdown any currently-active solver process. --   A fresh solver process will be started on the --   next call to `getSolverProcess`.@@ -497,30 +338,13 @@ withSolverConn bak k = withSolverProcess bak (pure ()) (k . solverConn)  --- | Result of attempting to branch on a predicate.-data BranchResult-     -- | The both branches of the predicate might be satisfiable-     --   (although satisfiablility of either branch is not guaranteed).-   = IndeterminateBranchResult--     -- | Commit to the branch where the given predicate is equal to-     --   the returned boolean.  The opposite branch is unsatisfiable-     --   (although the given branch is not necessarily satisfiable).-   | NoBranch !Bool--     -- | The context before considering the given predicate was already-     --   unsatisfiable.-   | UnsatisfiableContext-   deriving (Data, Eq, Generic, Ord, Typeable)-- restoreAssumptionFrames ::   OnlineSolver solver =>   OnlineBackend solver scope st fs ->   SolverProcess scope solver ->-  AssumptionFrames (CrucibleAssumptions (B.Expr scope)) ->+  AS.AssumptionFrames (CrucibleAssumptions (B.Expr scope)) ->   IO ()-restoreAssumptionFrames bak proc (AssumptionFrames base frms) =+restoreAssumptionFrames bak proc (AS.AssumptionFrames base frms) =   do let sym = onlineExprBuilder bak      -- assume the base-level assumptions      SMT.assume (solverConn proc) =<< assumptionsPred sym base@@ -530,29 +354,6 @@       do push proc          SMT.assume (solverConn proc) =<< assumptionsPred sym frm -considerSatisfiability ::-  OnlineSolver solver =>-  OnlineBackend solver scope st fs ->-  Maybe ProgramLoc ->-  B.BoolExpr scope ->-  IO BranchResult-considerSatisfiability bak mbPloc p =-  let sym = onlineExprBuilder bak in-  withSolverProcess bak (pure IndeterminateBranchResult) $ \proc ->-   do pnot <- notPred sym p-      let locDesc = case mbPloc of-            Just ploc -> show (plSourceLoc ploc)-            Nothing -> "(unknown location)"-      let rsn = "branch sat: " ++ locDesc-      p_res <- checkSatisfiable proc rsn p-      pnot_res <- checkSatisfiable proc rsn pnot-      case (p_res, pnot_res) of-        (Unsat{}, Unsat{}) -> return UnsatisfiableContext-        (_      , Unsat{}) -> return (NoBranch True)-        (Unsat{}, _      ) -> return (NoBranch False)-        _                  -> return IndeterminateBranchResult-- instance HasSymInterface (B.ExprBuilder t st fs) (OnlineBackend solver t st fs) where   backendGetSym = onlineExprBuilder @@ -587,7 +388,7 @@          withSolverConn bak $ \conn -> SMT.assume conn p         -- Add assertions to list-       appendAssumptions as (assumptionStack bak)+       AS.appendAssumptions as (assumptionStack bak)    collectAssumptions bak =     AS.collectAssumptions (assumptionStack bak)@@ -596,11 +397,11 @@     -- NB, don't push a frame in the assumption stack unless     -- pushing to the solver succeeded     do withSolverProcess bak (pure ()) push-       pushFrame (assumptionStack bak)+       AS.pushFrame (assumptionStack bak)    popAssumptionFrame bak ident =     -- NB, pop the frame whether or not the solver pop succeeds-    do frm <- popFrame ident (assumptionStack bak)+    do frm <- AS.popFrame ident (assumptionStack bak)        withSolverProcess bak (pure ()) pop        return frm @@ -612,7 +413,7 @@    popAssumptionFrameAndObligations bak ident = do     -- NB, pop the frames whether or not the solver pop succeeds-    do frmAndGls <- popFrameAndGoals ident (assumptionStack bak)+    do frmAndGls <- AS.popFrameAndGoals ident (assumptionStack bak)        withSolverProcess bak (pure ()) pop        return frmAndGls @@ -629,3 +430,185 @@     do restoreSolverState bak gc        -- restore the previous assumption stack        AS.restoreAssumptionStack gc (assumptionStack bak)++  getBackendState bak = readIORef (AS.proofObligations (assumptionStack bak))++--------------------------------------------------------------------------------+-- Branch satisfiability++-- | Result of attempting to branch on a predicate.+data BranchResult+     -- | The both branches of the predicate might be satisfiable+     --   (although satisfiablility of either branch is not guaranteed).+   = IndeterminateBranchResult++     -- | Commit to the branch where the given predicate is equal to+     --   the returned boolean.  The opposite branch is unsatisfiable+     --   (although the given branch is not necessarily satisfiable).+   | NoBranch !Bool++     -- | The context before considering the given predicate was already+     --   unsatisfiable.+   | UnsatisfiableContext+   deriving (Data, Eq, Generic, Ord, Typeable)++considerSatisfiability ::+  OnlineSolver solver =>+  OnlineBackend solver scope st fs ->+  Maybe ProgramLoc ->+  B.BoolExpr scope ->+  IO BranchResult+considerSatisfiability bak mbPloc p =+  let sym = onlineExprBuilder bak in+  withSolverProcess bak (pure IndeterminateBranchResult) $ \proc ->+   do pnot <- notPred sym p+      let locDesc = case mbPloc of+            Just ploc -> show (plSourceLoc ploc)+            Nothing -> "(unknown location)"+      let rsn = "branch sat: " ++ locDesc+      p_res <- checkSatisfiable proc rsn p+      pnot_res <- checkSatisfiable proc rsn pnot+      case (p_res, pnot_res) of+        (Unsat{}, Unsat{}) -> return UnsatisfiableContext+        (_      , Unsat{}) -> return (NoBranch True)+        (Unsat{}, _      ) -> return (NoBranch False)+        _                  -> return IndeterminateBranchResult++--------------------------------------------------------------------------------+-- Backends for different solvers++type YicesOnlineBackend scope st fs = OnlineBackend Yices.Connection scope st fs++-- | Do something with a Yices online backend.+--   The backend is only valid in the continuation.+--+--   The Yices configuration options will be automatically+--   installed into the backend configuration object.+withYicesOnlineBackend ::+  (MonadIO m, MonadMask m) =>+  B.ExprBuilder scope st fs ->+  UnsatFeatures ->+  ProblemFeatures ->+  (YicesOnlineBackend scope st fs -> m a) ->+  m a+withYicesOnlineBackend sym unsatFeat extraFeatures action =+  let feat = Yices.yicesDefaultFeatures .|. unsatFeaturesToProblemFeatures unsatFeat  .|. extraFeatures in+  withOnlineBackend sym feat $ \bak ->+    do liftIO $ tryExtendConfig Yices.yicesOptions (getConfiguration sym)+       action bak++type Z3OnlineBackend scope st fs = OnlineBackend (SMT2.Writer Z3.Z3) scope st fs++-- | Do something with a Z3 online backend.+--   The backend is only valid in the continuation.+--+--   The Z3 configuration options will be automatically+--   installed into the backend configuration object.+withZ3OnlineBackend ::+  (MonadIO m, MonadMask m) =>+  B.ExprBuilder scope st fs ->+  UnsatFeatures ->+  ProblemFeatures ->+  (Z3OnlineBackend scope st fs -> m a) ->+  m a+withZ3OnlineBackend sym unsatFeat extraFeatures action =+  let feat = (SMT2.defaultFeatures Z3.Z3 .|. unsatFeaturesToProblemFeatures unsatFeat .|. extraFeatures) in+  withOnlineBackend sym feat $ \bak ->+    do liftIO $ tryExtendConfig Z3.z3Options (getConfiguration sym)+       action bak++type BitwuzlaOnlineBackend scope st fs = OnlineBackend (SMT2.Writer Bitwuzla.Bitwuzla) scope st fs++-- | Do something with a Bitwuzla online backend.+--   The backend is only valid in the continuation.+--+--   The Bitwuzla configuration options will be automatically+--   installed into the backend configuration object.+withBitwuzlaOnlineBackend ::+  (MonadIO m, MonadMask m) =>+  B.ExprBuilder scope st fs ->+  UnsatFeatures ->+  ProblemFeatures ->+  (BitwuzlaOnlineBackend scope st fs -> m a) ->+  m a+withBitwuzlaOnlineBackend sym unsatFeat extraFeatures action =+  let feat = (SMT2.defaultFeatures Bitwuzla.Bitwuzla .|. unsatFeaturesToProblemFeatures unsatFeat .|. extraFeatures) in+  withOnlineBackend sym feat $ \bak -> do+    liftIO $ tryExtendConfig Bitwuzla.bitwuzlaOptions (getConfiguration sym)+    action bak++type BoolectorOnlineBackend scope st fs = OnlineBackend (SMT2.Writer Boolector.Boolector) scope st fs++-- | Do something with a Boolector online backend.+--   The backend is only valid in the continuation.+--+--   The Boolector configuration options will be automatically+--   installed into the backend configuration object.+withBoolectorOnlineBackend ::+  (MonadIO m, MonadMask m) =>+  B.ExprBuilder scope st fs ->+  UnsatFeatures ->+  (BoolectorOnlineBackend scope st fs -> m a) ->+  m a+withBoolectorOnlineBackend sym unsatFeat action =+  let feat = (SMT2.defaultFeatures Boolector.Boolector .|. unsatFeaturesToProblemFeatures unsatFeat) in+  withOnlineBackend sym feat $ \bak -> do+    liftIO $ tryExtendConfig Boolector.boolectorOptions (getConfiguration sym)+    action bak++type CVC4OnlineBackend scope st fs = OnlineBackend (SMT2.Writer CVC4.CVC4) scope st fs++-- | Do something with a CVC4 online backend.+--   The backend is only valid in the continuation.+--+--   The CVC4 configuration options will be automatically+--   installed into the backend configuration object.+withCVC4OnlineBackend ::+  (MonadIO m, MonadMask m) =>+  B.ExprBuilder scope st fs ->+  UnsatFeatures ->+  ProblemFeatures ->+  (CVC4OnlineBackend scope st fs -> m a) ->+  m a+withCVC4OnlineBackend sym unsatFeat extraFeatures action =+  let feat = (SMT2.defaultFeatures CVC4.CVC4 .|. unsatFeaturesToProblemFeatures unsatFeat .|. extraFeatures) in+  withOnlineBackend sym feat $ \bak -> do+    liftIO $ tryExtendConfig CVC4.cvc4Options (getConfiguration sym)+    action bak++type CVC5OnlineBackend scope st fs = OnlineBackend (SMT2.Writer CVC5.CVC5) scope st fs++-- | Do something with a CVC5 online backend.+--   The backend is only valid in the continuation.+--+--   The CVC5 configuration options will be automatically+--   installed into the backend configuration object.+withCVC5OnlineBackend ::+  (MonadIO m, MonadMask m) =>+  B.ExprBuilder scope st fs ->+  UnsatFeatures ->+  ProblemFeatures ->+  (CVC5OnlineBackend scope st fs -> m a) ->+  m a+withCVC5OnlineBackend sym unsatFeat extraFeatures action =+  let feat = (SMT2.defaultFeatures CVC5.CVC5 .|. unsatFeaturesToProblemFeatures unsatFeat .|. extraFeatures) in+  withOnlineBackend sym feat $ \bak -> do+    liftIO $ tryExtendConfig CVC5.cvc5Options (getConfiguration sym)+    action bak++type STPOnlineBackend scope st fs = OnlineBackend (SMT2.Writer STP.STP) scope st fs++-- | Do something with a STP online backend.+--   The backend is only valid in the continuation.+--+--   The STO configuration options will be automatically+--   installed into the backend configuration object.+withSTPOnlineBackend ::+  (MonadIO m, MonadMask m) =>+  B.ExprBuilder scope st fs ->+  (STPOnlineBackend scope st fs -> m a) ->+  m a+withSTPOnlineBackend sym action =+  withOnlineBackend sym (SMT2.defaultFeatures STP.STP) $ \bak -> do+    liftIO $ tryExtendConfig STP.stpOptions (getConfiguration sym)+    action bak
src/Lang/Crucible/Backend/ProofGoals.hs view
@@ -3,10 +3,11 @@ Copyright   : (c) Galois, Inc 2014-2018 License     : BSD3 -This module defines a data strucutre for storing a collection of-proof obligations, and the current state of assumptions.+This module defines a data structure ('GoalCollector') for storing the current+state of assumptions and a collection of proof obligations. -} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-}@@ -22,6 +23,7 @@     -- * Goal collector   , FrameIdentifier(..), GoalCollector   , emptyGoalCollector+  , ppGoalCollector      -- ** traversals   , traverseGoalCollector@@ -29,7 +31,7 @@      -- ** Context management   , gcAddAssumes, gcProve-  , gcPush, gcPop, gcAddGoals,+  , gcPush, gcPop, gcAddGoals, gcAddTopLevelAssume,      -- ** Global operations on context     gcRemoveObligations, gcRestore, gcReset, gcFinish@@ -40,9 +42,11 @@   where  import           Control.Monad.Reader+import qualified Data.Foldable as F import           Data.Sequence (Seq) import qualified Data.Sequence as Seq import           Data.Word (Word64)+import qualified Prettyprinter as PP  import           Lang.Crucible.Backend.Goals @@ -55,16 +59,80 @@  deriving(Eq,Ord,Show)  --- | A data-strucutre that can incrementally collect goals in context.+-- | A data-structure that can incrementally collect goals in context. --   It keeps track both of the collection of assumptions that lead to --   the current state, as well as any proof obligations incurred along --   the way.+--+--   The main use of 'GoalCollector' is as the state of an+--   'Lang.Crucible.Backend.AssumptionStack.AssumptionStack', which itself is+--   part of the state of the simple and online backends.+--+--   'GoalCollector' can be somewhat counter-intuitive. The "top"+--   ('TopCollector') is the *leaf* when 'GoalCollector' is considered as+--   a tree (which is a common way to conceptualize recursive algebraic+--   data types such as this one). A 'GoalCollector' is shaped like a+--   cons-list with three different cons-like constructors ('CollectorFrame',+--   'CollectingAssumptions', and 'CollectingGoals') and one nil-like+--   constructor 'TopCollector'. That is to say, a 'GoalCollector' is a sequence+--   that always ends in a single 'TopCollector'.+--+--   Furthermore, the frame identified by the first ('FrameIdentifier') argument+--   of 'CollectorFrame' does not conceptually contain the goals *inside* the+--   second ('GoalCollector') argument, but rather contains all the assumptions+--   and goals in whatever 'GoalCollector' *contains* the 'CollectorFrame'+--   constructor (everything *outside* of the 'CollectorFrame'). Concretely, in+--   the expression+--   @+--   'CollectingGoals' gls ('CollectingAssumptions' asmps ('CollectorFrame' frm ('TopCollector' gls0)))+--   @+--   the goals @gls@ and assumptions @asmps@ are in the frame @frm@, rather than+--   the top-level goals @gls0@.+--+--   This inside-out structure is reflected in the pretty-printer+--   'ppGoalCollector' below. The Crucible-CLI test-case @assumption-state@+--   shows this pretty-printer in action in a Crucible program with branching,+--   which can be helpful in understanding 'GoalCollector'. data GoalCollector asmp goal   = TopCollector !(Seq (Goals asmp goal))   | CollectorFrame !FrameIdentifier !(GoalCollector asmp goal)   | CollectingAssumptions !asmp !(GoalCollector asmp goal)   | CollectingGoals !(Seq (Goals asmp goal)) !(GoalCollector asmp goal) +ppGoalCollector ::+  forall asmp goal ann.+  (asmp -> PP.Doc ann) ->+  (goal -> PP.Doc ann) ->+  GoalCollector asmp goal ->+  PP.Doc ann+ppGoalCollector ppAsmp ppGoal = go mempty+  where+    go :: PP.Doc ann -> GoalCollector asmp goal -> PP.Doc ann+    go remainder =+      \case+        TopCollector gls ->+          PP.vcat+          [ PP.pretty "Top-level goals:"+          , PP.list (map (ppGoals ppAsmp ppGoal) (F.toList gls))+          , remainder+          ]+        CollectorFrame (FrameIdentifier fid) gc ->+          let pLines = [PP.pretty "Frame " <> PP.viaShow fid <> PP.pretty ":", remainder] in+          go (PP.hang 2 (PP.vcat pLines)) gc+        CollectingAssumptions asmp gc ->+          let pLines = [PP.pretty "Assumptions:" , ppAsmp asmp, remainder] in+          go (PP.hang 2 (PP.vcat pLines)) gc+        CollectingGoals gls gc ->+          let pLines = [ PP.pretty "Prove all:"+                       , PP.list (map (ppGoals ppAsmp ppGoal) (F.toList gls))+                       , remainder+                       ] in+          go (PP.hang 2 (PP.vcat pLines)) gc++-- | Intended for debugging, this is not generally a user-facing datatype.+instance (PP.Pretty asmp, PP.Pretty goal) => PP.Pretty (GoalCollector asmp goal) where+  pretty = ppGoalCollector PP.pretty PP.pretty+ -- | A collector with no goals and no context. emptyGoalCollector :: GoalCollector asmp goal emptyGoalCollector = TopCollector mempty@@ -141,6 +209,30 @@ gcAddGoals g (TopCollector gs) = TopCollector (gs Seq.|> g) gcAddGoals g (CollectingGoals gs gc) = CollectingGoals (gs Seq.|> g) gc gcAddGoals g gc = CollectingGoals (Seq.singleton g) gc++-- | Add an assumption that is in scope for all goals, even ones in earlier+-- frames.+gcAddTopLevelAssume ::+  Monoid asmp =>+  asmp ->+  GoalCollector asmp goal ->+  GoalCollector asmp goal+gcAddTopLevelAssume asmp =+  \case+    TopCollector gls ->+      -- Syntactically, it appears that `asmp` is duplicated here, perhaps+      -- unnecessarily. In fact, this is necessary. The `CollectingAssumptions`+      -- constructor brings the assumption into scope for all the goals+      -- *outside* of the top-level (see the comment on `GoalCollector` for+      -- the "inside-out" structure of `GoalCollector`), whereas the `assuming`+      -- brings it into scope for top-level goals.+      CollectingAssumptions asmp (TopCollector (assuming asmp <$> gls))+    CollectorFrame frm gc ->+      CollectorFrame frm (gcAddTopLevelAssume asmp gc)+    CollectingAssumptions asmp' gc ->+      CollectingAssumptions asmp' (gcAddTopLevelAssume asmp gc)+    CollectingGoals gls gc ->+      CollectingGoals gls (gcAddTopLevelAssume asmp gc)  -- | Add a new proof obligation to the current context. gcProve :: goal -> GoalCollector asmp goal -> GoalCollector asmp goal
src/Lang/Crucible/Backend/Simple.hs view
@@ -4,13 +4,13 @@ -- Description : The "simple" solver backend -- Copyright   : (c) Galois, Inc 2015-2016 -- License     : BSD3--- Maintainer  : Rob Dockins <rdockins@galois.com>+-- Maintainer  : Ryan Scott <rscott@galois.com>, Langston Barrett <langston@galois.com> -- Stability   : provisional ----- An "offline" backend for communicating with solvers.  This backend--- does not maintain a persistent connection to a solver, and does--- not perform satisfiability checks at symbolic branch points.-------------------------------------------------------------------------+-- An "offline" backend for communicating with SMT solvers. In contrast to+-- "Lang.Crucible.Backend.Online", this backend does not maintain a persistent+-- connection to a solver.+-- ----------------------------------------------------------------------  {-# LANGUAGE GADTs #-} {-# LANGUAGE FlexibleContexts #-}@@ -32,6 +32,7 @@  import           Control.Lens ( (^.) ) import           Control.Monad (void)+import           Data.IORef (readIORef)  import           What4.Config import           What4.Interface@@ -42,7 +43,7 @@ import           Lang.Crucible.Simulator.SimError  --------------------------------------------------------------------------- SimpleBackendState+-- SimpleBackend  -- | This represents the state of the backend along a given execution. -- It contains the current assertion stack.@@ -111,3 +112,5 @@    restoreAssumptionState bak newstk = do     AS.restoreAssumptionStack newstk (sbAssumptionStack bak)++  getBackendState bak = readIORef (AS.proofObligations (sbAssumptionStack bak))
src/Lang/Crucible/Concretize.hs view
@@ -7,15 +7,27 @@ -- Maintainer       : Langston Barrett <langston@galois.com> -- Stability        : provisional ----- This module defines 'concRegValue', a function that takes a 'RegValue' (i.e.,--- a symbolic value), and a model from the SMT solver ('W4GE.GroundEvalFn'), and--- returns the concrete value that the symbolic value takes in the model.+-- This module defines three different kinds of functions. In order of how much+-- work they perform: ----- This can be used to report specific values that lead to violations of--- assertions, including safety assertions.+-- * /Grounding/ functions (e.g., 'groundRegValue') take symbolic values+--   ('RegValue's) and a model from an SMT solver ('W4GE.GroundEvalFn'), and+--   return the concrete value ('ConcRegValue') that the symbolic value takes in+--   the model. These functions can be used to report specific values that lead+--   to violations of assertions, including safety assertions.+-- * /Concretization/ functions (e.g., 'concRegValue') request a model that is+--   consistent with the current assumptions (e.g., path conditions) from the+--   symbolic backend, and then ground a value in that model. These can be used+--   to reduce the size and complexity of later queries to SMT solvers, at the+--   cost of no longer being sound from a verification standpoint.+-- * /Unique concretization/ functions (e.g., 'uniquelyConcRegValue') do the+--   same thing as concretization functions, but then check if the concrete+--   value is the /only possible/ value for the given symbolic expression in+--   /any/ model. ------------------------------------------------------------------------  {-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE StandaloneKindSignatures #-}@@ -30,24 +42,38 @@ module Lang.Crucible.Concretize   ( ConcRegValue   , ConcRV'(..)+  , asConcRegValue+  , asConcRegEntry+  , asConcRegMap   , ConcAnyValue(..)   , ConcIntrinsic   , IntrinsicConcFn(..)   , ConcCtx(..)+    -- * Grounding+  , groundRegValue+  , groundRegEntry+  , groundRegMap+    -- * Concretization   , concRegValue   , concRegEntry   , concRegMap+    -- * Unique concretization+  , uniquelyConcRegValue+  , uniquelyConcRegEntry+  , uniquelyConcRegMap     -- * There and back again   , IntrinsicConcToSymFn(..)   , concToSym   ) where  import qualified Data.Foldable as F+import           Data.Functor.Const (Const(..)) import           Data.Kind (Type) import           Data.List.NonEmpty (NonEmpty) import qualified Data.List.NonEmpty as NE import           Data.Map (Map) import qualified Data.Map as Map+import           Data.Proxy (Proxy(Proxy)) import           Data.Sequence (Seq) import           Data.Text (Text) import qualified Data.Text as Text@@ -57,14 +83,20 @@ import qualified Data.Parameterized.Context as Ctx import           Data.Parameterized.Map (MapF) import qualified Data.Parameterized.Map as MapF-import           Data.Parameterized.TraversableFC (traverseFC)+import           Data.Parameterized.TraversableFC (traverseFC, foldlMFC) +import qualified What4.Concretize as W4C+import qualified What4.Config as W4Cfg import           What4.Expr (Expr, ExprBuilder, Flags, FloatModeRepr(..)) import qualified What4.Expr.GroundEval as W4GE import           What4.Interface (SymExpr) import qualified What4.Interface as W4I import qualified What4.Partial as W4P+import qualified What4.Protocol.Online as WPO+import qualified What4.SatResult as WSat +import qualified Lang.Crucible.Backend as CB+import qualified Lang.Crucible.Backend.Online as CBO import           Lang.Crucible.FunctionHandle (FnHandle, RefCell) import           Lang.Crucible.Simulator.Intrinsics (Intrinsic) import           Lang.Crucible.Simulator.RegMap (RegEntry, RegMap)@@ -84,7 +116,7 @@  -- | Defines the \"concrete\" interpretations of 'CrucibleType' (as opposed -- to the \"symbolic\" interpretations, which are defined by 'RegValue'), as--- returned by 'concRegValue'.+-- returned by 'groundRegValue'. -- -- Unlike What4\'s 'W4GE.GroundValue', this type family is parameterized -- by @sym@, the symbolic backend. This is because Crucible makes use of@@ -112,10 +144,40 @@   ConcRegValue sym (IntrinsicType nm ctx) = ConcIntrinsic nm ctx   ConcRegValue sym (StringMapType tp) = Map Text (ConcRV' sym tp) +-- | Check if a 'RegValue' is actually concrete+asConcRegValue ::+  W4I.IsExpr (SymExpr sym) =>+  proxy sym ->+  TypeRepr tp ->+  RegValue sym tp ->+  Maybe (ConcRegValue sym tp)+asConcRegValue _proxy tp val =+  -- TODO: More cases could be added here.+  case asBaseType tp of+    AsBaseType {} -> W4GE.asGround val+    _ -> Nothing++-- | Check if a 'RM.RegEntry' is actually concrete+asConcRegEntry ::+  forall sym tp.+  W4I.IsExpr (SymExpr sym) =>+  RM.RegEntry sym tp ->+  Maybe (ConcRegValue sym tp)+asConcRegEntry (RM.RegEntry t v) = asConcRegValue (Proxy @sym) t v++-- | Check if a 'RM.RegMap' is actually concrete+asConcRegMap ::+  forall sym tp.+  W4I.IsExpr (SymExpr sym) =>+  RM.RegMap sym tp ->+  Maybe (Ctx.Assignment (ConcRV' sym) tp)+asConcRegMap (RM.RegMap assign) =+  traverseFC (\re -> ConcRV' <$> asConcRegEntry re) assign+ --------------------------------------------------------------------- -- * ConcCtx --- | Context needed for 'concRegValue'+-- | Context needed for 'groundRegValue' -- -- The @t@ parameter matches that on 'W4GE.GroundEvalFn' and 'Expr', namely, it -- is a phantom type brand used to relate nonces to a specific nonce generator@@ -172,7 +234,7 @@   W4P.Partial (W4I.Pred sym) (RegValue sym tp) ->   IO (Maybe (ConcRegValue sym tp)) concPartial ctx tp (W4P.Partial p v) =-  iteIO ctx p (Just <$> concRegValue ctx tp v) (pure Nothing)+  iteIO ctx p (Just <$> groundRegValue ctx tp v) (pure Nothing)  -- | Helper, not exported concPartialWithErr ::@@ -224,13 +286,13 @@ --------------------------------------------------------------------- -- * Any --- | An 'AnyValue' concretized by 'concRegValue'+-- | An 'AnyValue' concretized by 'groundRegValue' data ConcAnyValue sym = forall tp. ConcAnyValue (TypeRepr tp) (ConcRV' sym tp)  --------------------------------------------------------------------- -- * FnVal --- | A 'FnVal' concretized by 'concRegValue'+-- | A 'FnVal' concretized by 'groundRegValue' data ConcFnVal (sym :: Type) (args :: Ctx CrucibleType) (res :: CrucibleType) where   ConcClosureFnVal ::     !(ConcFnVal sym (args ::> tp) ret) ->@@ -260,7 +322,7 @@   \case     RV.ClosureFnVal fv t v -> do       concV <- concFnVal ctx (args Ctx.:> t) ret fv-      v' <- concRegValue ctx t v+      v' <- groundRegValue ctx t v       pure (ConcClosureFnVal concV t (ConcRV' v'))     RV.VarargsFnVal hdl extra ->       pure (ConcVarargsFnVal hdl extra)@@ -306,7 +368,7 @@ concSymSequence ctx tp =   SymSeq.concretizeSymSequence     (ground ctx)-    (fmap ConcRV' . concRegValue ctx tp)+    (fmap ConcRV' . groundRegValue ctx tp)  --------------------------------------------------------------------- -- * StringMap@@ -331,7 +393,7 @@ --------------------------------------------------------------------- -- * Variant --- | Note that we do not attempt to \"normalize\" variants in 'concRegValue'+-- | Note that we do not attempt to \"normalize\" variants in 'groundRegValue' -- in any way. If the model reports that multiple branches of a variant are -- plausible, then multiple branches might be included as 'Just's. newtype ConcVariantBranch sym tp@@ -356,21 +418,21 @@         Nothing -> pure (ConcVariantBranch Nothing)  ------------------------------------------------------------------------ * 'concRegValue'+-- * 'groundRegValue'  -- | Pick a feasible concrete value from the model -- -- This function does not attempt to \"normalize\" variants nor mux trees in any -- way. If the model reports that multiple branches of a variant or mux tree are -- plausible, then multiple branches might be included in the result.-concRegValue ::+groundRegValue ::   (SymExpr sym ~ Expr t) =>   W4I.IsExprBuilder sym =>   ConcCtx sym t ->   TypeRepr tp ->   RegValue sym tp ->   IO (ConcRegValue sym tp)-concRegValue ctx tp v =+groundRegValue ctx tp v =   case (tp, v) of     -- Base types     (BoolRepr, _) -> ground ctx v@@ -391,13 +453,13 @@      -- Simple recursive cases     (AnyRepr, RV.AnyValue tp' v') ->-      ConcAnyValue tp' . ConcRV' <$> concRegValue ctx tp' v'+      ConcAnyValue tp' . ConcRV' <$> groundRegValue ctx tp' v'     (RecursiveRepr symb tyCtx, RV.RolledType v') ->-      concRegValue ctx (unrollType symb tyCtx) v'+      groundRegValue ctx (unrollType symb tyCtx) v'     (StructRepr tps, _) ->-      Ctx.zipWithM (\tp' (RV.RV v') -> ConcRV' <$> concRegValue ctx tp' v') tps v+      Ctx.zipWithM (\tp' (RV.RV v') -> ConcRV' <$> groundRegValue ctx tp' v') tps v     (VectorRepr tp', _) ->-      traverse (fmap ConcRV' . concRegValue ctx tp') v+      traverse (fmap ConcRV' . groundRegValue ctx tp') v      -- Cases with helper functions     (MaybeRepr tp', _) ->@@ -422,26 +484,209 @@     -- Incomplete cases     (WordMapRepr _ _, _) -> pure () --- | Like 'concRegValue', but for 'RegEntry'-concRegEntry ::+-- | Like 'groundRegValue', but for 'RegEntry'+groundRegEntry ::   (SymExpr sym ~ Expr t) =>   W4I.IsExprBuilder sym =>   ConcCtx sym t ->   RegEntry sym tp ->   IO (ConcRegValue sym tp)-concRegEntry ctx e = concRegValue ctx (RM.regType e) (RM.regValue e)+groundRegEntry ctx e = groundRegValue ctx (RM.regType e) (RM.regValue e) --- | Like 'concRegEntry', but for a whole 'RegMap'-concRegMap ::+-- | Like 'groundRegEntry', but for a whole 'RegMap'+groundRegMap ::   (SymExpr sym ~ Expr t) =>   W4I.IsExprBuilder sym =>   ConcCtx sym t ->   RegMap sym tps ->   IO (Ctx.Assignment (ConcRV' sym) tps)-concRegMap ctx (RM.RegMap m) = traverseFC (fmap ConcRV' . concRegEntry ctx) m+groundRegMap ctx (RM.RegMap m) = traverseFC (fmap ConcRV' . groundRegEntry ctx) m  ------------------------------------------------------------------------ * concToSym+-- * 'concRegValue'++-- | Generate a model and pick a feasible concrete value from it+concRegValue ::+  forall tp sym bak solver scope st fs.+  ( CB.IsSymBackend sym bak+  , sym ~ ExprBuilder scope st fs+  , SymExpr sym ~ Expr scope+  , bak ~ CBO.OnlineBackend solver scope st fs+  , WPO.OnlineSolver solver+  ) =>+  bak ->+  MapF SymbolRepr (IntrinsicConcFn scope) ->+  TypeRepr tp ->+  RegValue sym tp ->+  IO (Either W4C.ConcretizationFailure (ConcRegValue sym tp))+concRegValue bak iFns tp v = concRegEntry bak iFns (RM.RegEntry tp v)++-- | Generate a model and pick a feasible concrete value from it+concRegEntry ::+  forall tp sym bak solver scope st fs.+  ( CB.IsSymBackend sym bak+  , sym ~ ExprBuilder scope st fs+  , SymExpr sym ~ Expr scope+  , bak ~ CBO.OnlineBackend solver scope st fs+  , WPO.OnlineSolver solver+  ) =>+  bak ->+  MapF SymbolRepr (IntrinsicConcFn scope) ->+  RM.RegEntry sym tp ->+  IO (Either W4C.ConcretizationFailure (ConcRegValue sym tp))+concRegEntry bak iFns re = do+  res <- concRegMap bak iFns (RM.RegMap (Ctx.singleton re))+  case res of+    Left e -> pure (Left e)+    Right (Ctx.Empty Ctx.:> ConcRV' concV) -> pure (Right concV)++-- | Like 'concRegValue', but for a whole 'RegMap'+concRegMap ::+  forall tps sym bak solver scope st fs.+  ( CB.IsSymBackend sym bak+  , sym ~ ExprBuilder scope st fs+  , SymExpr sym ~ Expr scope+  , bak ~ CBO.OnlineBackend solver scope st fs+  , WPO.OnlineSolver solver+  ) =>+  bak ->+  MapF SymbolRepr (IntrinsicConcFn scope) ->+  RegMap sym tps ->+  IO (Either W4C.ConcretizationFailure (Ctx.Assignment (ConcRV' sym) tps))+concRegMap bak iFns m = do+  case asConcRegMap m of+    Just concM -> pure (Right concM)+    Nothing ->+      withEnabledOnline $ do+        let err = panic "concRegValue" ["requires online solving to be enabled"]+        cond <- CB.getPathCondition bak+        CBO.withSolverProcess bak err $ \sp -> do+          msat <- WPO.checkWithAssumptionsAndModel sp "concRegValue" [cond]+          case msat of+            WSat.Unknown -> pure $ Left W4C.SolverUnknown+            WSat.Unsat {} -> pure $ Left W4C.UnsatInitialAssumptions+            WSat.Sat mdl -> do+              let ctx = ConcCtx { model = mdl, intrinsicConcFuns = iFns }+              expr <- groundRegMap @sym ctx m+              pure (Right expr)+  where+    withEnabledOnline f = do+      let sym = CB.backendGetSym bak+      let conf = W4I.getConfiguration sym+      enabledOpt <- W4Cfg.getOptionSetting CBO.enableOnlineBackend conf+      wasEnabled <- W4Cfg.getOpt enabledOpt+      _ <- W4Cfg.setOpt enabledOpt True+      r <- f+      _ <- W4Cfg.setOpt enabledOpt wasEnabled+      pure r++---------------------------------------------------------------------+-- * 'uniquelyConcRegValue'++-- | Generate a model and pick a feasible concrete value from it+uniquelyConcRegValue ::+  forall tp sym bak solver scope st fm.+  ( CB.IsSymBackend sym bak+  , sym ~ ExprBuilder scope st (Flags fm)+  , SymExpr sym ~ Expr scope+  , bak ~ CBO.OnlineBackend solver scope st (Flags fm)+  , WPO.OnlineSolver solver+  ) =>+  bak ->+  FloatModeRepr fm ->+  MapF SymbolRepr (IntrinsicConcFn scope) ->+  MapF SymbolRepr IntrinsicConcToSymFn ->+  TypeRepr tp ->+  RegValue sym tp ->+  IO (Either W4C.UniqueConcretizationFailure (ConcRegValue sym tp))+uniquelyConcRegValue bak fm iFns sFns tp v =+  uniquelyConcRegEntry bak fm iFns sFns (RM.RegEntry tp v)++-- | Generate a model and pick a feasible concrete value from it+uniquelyConcRegEntry ::+  forall tp sym bak solver scope st fm.+  ( CB.IsSymBackend sym bak+  , sym ~ ExprBuilder scope st (Flags fm)+  , SymExpr sym ~ Expr scope+  , bak ~ CBO.OnlineBackend solver scope st (Flags fm)+  , WPO.OnlineSolver solver+  ) =>+  bak ->+  FloatModeRepr fm ->+  MapF SymbolRepr (IntrinsicConcFn scope) ->+  MapF SymbolRepr IntrinsicConcToSymFn ->+  RM.RegEntry sym tp ->+  IO (Either W4C.UniqueConcretizationFailure (ConcRegValue sym tp))+uniquelyConcRegEntry bak fm iFns sFns re = do+  res <- uniquelyConcRegMap bak fm iFns sFns (RM.RegMap (Ctx.singleton re))+  case res of+    Left e -> pure (Left e)+    Right (Ctx.Empty Ctx.:> ConcRV' concV) -> pure (Right concV)++-- | Like 'concRegValue', but for a whole 'RegMap'+uniquelyConcRegMap ::+  forall tps sym bak solver scope st fm.+  ( CB.IsSymBackend sym bak+  , sym ~ ExprBuilder scope st (Flags fm)+  , SymExpr sym ~ Expr scope+  , bak ~ CBO.OnlineBackend solver scope st (Flags fm)+  , WPO.OnlineSolver solver+  ) =>+  bak ->+  FloatModeRepr fm ->+  MapF SymbolRepr (IntrinsicConcFn scope) ->+  MapF SymbolRepr IntrinsicConcToSymFn ->+  RegMap sym tps ->+  IO (Either W4C.UniqueConcretizationFailure (Ctx.Assignment (ConcRV' sym) tps))+uniquelyConcRegMap bak fm iFns sFns m = do+  case asConcRegMap m of+    Just concM -> pure (Right concM)+    Nothing -> do+      -- First, check to see if there are a models of the symbolic values.+      concM_ <- concRegMap bak iFns m+      case concM_ of+        Left e -> pure (Left (W4C.GroundingFailure e))+        Right concM -> do+          -- We found a model, so check to see if this is the only possible+          -- model for these symbolic values.  We do this by adding a blocking+          -- clause that assumes the `RegValue`s are /not/ equal to the+          -- model we found in the previous step. If this is unsatisfiable,+          -- the `RegValue`s can only be equal to the first model, so we can+          -- conclude they are concrete. If it is satisfiable, on the other+          -- hand, the `RegValue`s can be multiple values, so they are truly+          -- symbolic.+          let sym = CB.backendGetSym bak+          let notEq ::+                forall tp.+                ConcRV' sym tp ->+                RM.RegEntry sym tp ->+                IO (Const (W4I.Pred sym) tp)+              notEq (ConcRV' concV) (RM.RegEntry tp v) = do+                symV <- concToSym sym sFns fm tp concV+                p <- W4I.notPred sym =<< RV.eqRegValue sym tp symV v+                pure (Const p)+          let RM.RegMap mAssign = m+          preds <- Ctx.zipWithM notEq concM mAssign+          p <-+            foldlMFC+              (\p (Const p') -> W4I.andPred sym p p')+              (W4I.truePred sym)+              preds++          frm <- CB.pushAssumptionFrame bak+          loc <- W4I.getCurrentProgramLoc sym+          CB.addAssumption bak (CB.GenericAssumption loc "uniquelyConcRegMap" p)+          concM_' <- concRegMap bak iFns m+          res <-+            case concM_' of+              Left W4C.UnsatInitialAssumptions -> pure (Right concM)+              Left e -> pure (Left (W4C.GroundingFailure e))+              Right _ -> pure (Left W4C.MultipleModels)+          _ <- CB.popAssumptionFrame bak frm+          pure res++---------------------------------------------------------------------+-- * 'concToSym'  -- | Function for re-symbolizing an intrinsic type type IntrinsicConcToSymFn :: Symbol -> Type
+ src/Lang/Crucible/README.hs view
@@ -0,0 +1,113 @@+{- | This module is only for documentation purposes, and provides a high+level overview of Crucible aimed at developers. -}+{-# OPTIONS_GHC -Wno-unused-imports #-}+module Lang.Crucible.README where++import What4.Interface+import What4.Expr.App+import What4.BaseTypes+import Lang.Crucible.Backend++import Lang.Crucible.Types+import Lang.Crucible.CFG.Expr+import Lang.Crucible.CFG.Core hiding (Expr)+import Lang.Crucible.CFG.SSAConversion++import Lang.Crucible.Simulator.RegValue+import Lang.Crucible.Simulator.RegMap+import Lang.Crucible.Simulator.ExecutionTree+import Lang.Crucible.Simulator.EvalStmt+import Lang.Crucible.Simulator.Evaluation+import Lang.Crucible.Simulator.Intrinsics++++-- * Crucible Types++{- $+The types of the Crucible language are defined in "Lang.Crucible.Types".+Types are encoded using [singletons](https://github.com/Galoisinc/parameterized-utils?tab=readme-ov-file#parameterized-types-motivation):++* 'CrucibleType' is the Haskell type-level description of all Crucible types+* 'TypeRepr' are the associated value-level singletons, which+  are used when we pass around types, or store them in data structures.+-}++-- * Crucible Values++{- $+The inhabitants of each type are specified via the type function 'RegValue'.+We also have 'RegValue'' which is just a @newtype@ wrapper around 'RegValue',+because in Haskell type families may not be partially applied but @newtype@s can.++An important subset of the Crucible types are the base types (see 'BaseToType'),+which is for the symbolic expression we can construct+(see 'SymExpr' in [what4](https://github.com/Galoisinc/what4)).+Only these types may contain variables. In practice, we always use @what4@'s+'Expr' type to represent symbolic expressions.++Also, in some cases we use 'RegEntry' which+is just a pair of a 'RegValue' and its associated 'TypeRepr'.++There's also 'BaseTerm', which is similar to 'RegEntry' but+for base types---it contains a @what4@'s 'BaseTypeRepr' and a value of the corresponding+base type (usually;  the type is parameterized on exactly what we package+with the type).+-}+  +-- * Crucible Programs+  +{- $+The program executed by the simulator is in the form of a control flow+graph (CFG).  A typical way to construct them is as follows:++  1. use the functions in "Lang.Crucible.CFG.Generator" to produce a CFG with +     assignments ("Lang.Crucible.CFG.Reg")+  2. use 'toSSA' to translate this to a CFG without assignments+     ("Lang.Crucible.CFG.Core")++The core 'CFG' contains basic blocks with 'Stmt's and terminated+by 'TermStmt'.  The expression language for the core 'Core.CFG' is+the type 'App'.+-}+  +-- * Symbolic Simulator+  +{- $+The state of a running simulator is described in "Lang.Crucible.Simulator.ExecutionTree":++  * 'ExecState' is the current state of execution.+     We start with 'InitialState', and keep performing steps until we get+     to a 'ResultState'.+  * As the simulator executes, it keeps track of its state in 'SimState',+    which is stored in the current `ExecState`.+  * 'SimContext' is the part of the state that persists across branches+    (e.g, after we explore the @then@ part of an @if@ statement, we'll+    roll back some of the state changes before simulating the @else@ part,+    but the data in 'SimContext's persists).  An important part of the+    'SimContext' is the simulator's backend ('_ctxBackend'), which is how the+    simulator communicates with a solver, and builds symbolic expressions+    ('IsSymBackend').+++To evaluate a 'CFG' we evaluate the statements as described in+"Lang.Crucible.Simulator.EvalStmt" (details in 'stepStmt', 'stepTerm').+Details about expressions evaluation are in 'evalApp' in "Lang.Crucible.Simulator.Evaluation".++A lot of useful functionality relevant to the simulator can be accessed+from module "Lang.Crucible.Simulator".++The simulator supports mutable global variables.  Our tools use one such+global to store a language specific memory model, which records information+about various memory operations.+-}++-- * Intrinsics++{- $++Crucible type may be extended using 'IntrinsicType's.  An intrinsic type is+a type-level string, which can be given meaning by making an instance of+'IntrinsicClass'.+-}+  
src/Lang/Crucible/Simulator/BoundedExec.hs view
@@ -183,7 +183,7 @@   Bool {- ^ Produce a proof obligation when resources are exhausted? -} ->   IO (GenericExecutionFeature sym) boundedExecFeature getLoopBounds generateSideConditions =-  do gvRef <- newIORef (error "Global variable for BoundedExecFrameData not initialized")+  do gvRef <- newIORef Nothing      return $ GenericExecutionFeature $ onStep gvRef   where@@ -202,48 +202,52 @@                        }   checkBackedge ::-   IORef BoundedExecGlobal ->+   IORef (Maybe BoundedExecGlobal) ->    Some (BlockID blocks) ->    BlockID blocks tgt_args ->    SymGlobalState sym ->    IO (SymGlobalState sym, Maybe Word64)- checkBackedge gvRef (Some bid_curr) bid_tgt globals =-   do gv <- readIORef gvRef-      case fromMaybe [] (lookupGlobal gv globals) of-        ( Right fbd : rest ) ->-          do let id_curr = Ctx.indexVal (blockIDIndex bid_curr)-             let id_tgt  = Ctx.indexVal (blockIDIndex bid_tgt)-             let m = frameWtoMap fbd-             case (Map.lookup id_curr m, Map.lookup id_tgt m) of-               (Just (cx, _cd), Just (tx, td)) | tx <= cx ->-                  do let cs       = frameBoundCounts fbd-                     let (cs', q) = incrementBoundCount cs td-                     let fbd'     = fbd{ frameBoundCounts = cs' }-                     let globals' = insertGlobal gv (Right fbd' : rest) globals-                     if q > frameBoundLimit fbd then-                       return (globals', Just (frameBoundLimit fbd))-                     else-                       return (globals', Nothing)--               _ -> return (globals, Nothing)-        _ -> return (globals, Nothing)+ checkBackedge gvRef (Some bid_curr) bid_tgt globals = do+   let err = panic "checkBackedge" ["Global not initialized"]+   currGv <- readIORef gvRef+   let gv = fromMaybe err currGv+   case fromMaybe [] (lookupGlobal gv globals) of+      (Right fbd : rest) -> do+        let id_curr = Ctx.indexVal (blockIDIndex bid_curr)+        let id_tgt = Ctx.indexVal (blockIDIndex bid_tgt)+        let m = frameWtoMap fbd+        case (Map.lookup id_curr m, Map.lookup id_tgt m) of+          (Just (cx, _cd), Just (tx, td)) | tx <= cx -> do+            let cs = frameBoundCounts fbd+            let (cs', q) = incrementBoundCount cs td+            let fbd' = fbd{frameBoundCounts = cs'}+            let globals' = insertGlobal gv (Right fbd' : rest) globals+            if q > frameBoundLimit fbd+              then+                return (globals', Just (frameBoundLimit fbd))+              else+                return (globals', Nothing)+          _ -> return (globals, Nothing)+      _ -> return (globals, Nothing)   modifyStackState ::-   IORef BoundedExecGlobal ->+   IORef (Maybe BoundedExecGlobal) ->    (SimState p sym ext rtp f args -> ExecState p sym ext rtp) ->    SimState p sym ext rtp f args ->    ([Either FunctionName FrameBoundData] -> [Either FunctionName FrameBoundData]) ->    IO (ExecutionFeatureResult p sym ext rtp)- modifyStackState gvRef mkSt st f =-   do gv <- readIORef gvRef-      let xs = case lookupGlobal gv (st ^. stateGlobals) of-                 Nothing -> error "bounded execution global not defined!"-                 Just v  -> v-      let st' = st & stateGlobals %~ insertGlobal gv (f xs)-      return (ExecutionFeatureModifiedState (mkSt st'))+ modifyStackState gvRef mkSt st f = do +    currGv <- readIORef gvRef+    let err = panic "modifyStackState" ["Global variable not initialized"]+    let gv = fromMaybe err currGv+    let xs = case lookupGlobal gv (st ^. stateGlobals) of+              Nothing -> panic "modifyStackState"  ["Global variable not defined!"]+              Just v  -> v+    let st' = st & stateGlobals %~ insertGlobal gv (f xs)+    return (ExecutionFeatureModifiedState (mkSt st'))   onTransition ::-   IORef BoundedExecGlobal ->+   IORef (Maybe BoundedExecGlobal) ->    BlockID blocks tgt_args ->    ControlResumption p sym ext rtp (CrucibleLang blocks ret) ->    SimState p sym ext rtp (CrucibleLang blocks ret) ('Just a) ->@@ -264,16 +268,21 @@        Nothing -> return (ExecutionFeatureModifiedState (ControlTransferState res st'))   onStep ::-   IORef BoundedExecGlobal ->+   IORef (Maybe BoundedExecGlobal) ->    ExecState p sym ext rtp ->    IO (ExecutionFeatureResult p sym ext rtp)   onStep gvRef = \case    InitialState simctx globals ah ret cont ->      do let halloc = simHandleAllocator simctx-        gv <- freshGlobalVar halloc (Text.pack "BoundedExecFrameData") knownRepr-        writeIORef gvRef gv-        let globals' = insertGlobal gv [Left "_init"] globals+        currGv <- readIORef gvRef+        ngv <- case currGv of +          Nothing -> do +            gv <- freshGlobalVar halloc (Text.pack "BoundedExecFrameData") knownRepr+            writeIORef gvRef (Just gv)+            pure gv+          Just gv -> pure gv+        let globals' = insertGlobal ngv [Left "_init"] globals         let simctx' = simctx{ ctxIntrinsicTypes = MapF.insert (knownSymbol @"BoundedExecFrameData") IntrinsicMuxFn (ctxIntrinsicTypes simctx) }         return (ExecutionFeatureModifiedState (InitialState simctx' globals' ah ret cont)) 
src/Lang/Crucible/Simulator/BoundedRecursion.hs view
@@ -81,26 +81,28 @@   IO (GenericExecutionFeature sym)  boundedRecursionFeature getRecursionBound generateSideConditions =-  do gvRef <- newIORef (error "Global variable for BoundedRecursionData not initialized")+  do gvRef <- newIORef Nothing      return $ GenericExecutionFeature $ onStep gvRef   where  popFrame ::-   IORef BoundedRecursionGlobal ->+   IORef (Maybe BoundedRecursionGlobal) ->    (SimState p sym ext rtp f args -> ExecState p sym ext rtp) ->    SimState p sym ext rtp f args ->    IO (ExecutionFeatureResult p sym ext rtp)- popFrame gvRef mkSt st =-   do gv <- readIORef gvRef-      case lookupGlobal gv (st ^. stateGlobals) of-        Nothing -> panic "bounded recursion" ["global not defined!"]-        Just [] -> panic "bounded recursion" ["pop on empty stack!"]-        Just (_:xs) ->-          do let st' = st & stateGlobals %~ insertGlobal gv xs-             return (ExecutionFeatureModifiedState (mkSt st'))+ popFrame gvRef mkSt st = do+   currGv <- readIORef gvRef+   let err = panic "bounded recursion" ["gv not initialized"]+   let gv = fromMaybe err currGv+   case lookupGlobal gv (st ^. stateGlobals) of+     Nothing -> panic "bounded recursion" ["global not defined!"]+     Just [] -> panic "bounded recursion" ["pop on empty stack!"]+     Just (_ : xs) -> do+       let st' = st & stateGlobals %~ insertGlobal gv xs+       return (ExecutionFeatureModifiedState (mkSt st'))   pushFrame ::-   IORef BoundedRecursionGlobal ->+   IORef (Maybe BoundedRecursionGlobal) ->    (BoundedRecursionMap -> BoundedRecursionMap -> [BoundedRecursionMap] -> [BoundedRecursionMap]) ->    SomeHandle ->    (SimState p sym ext rtp f args -> ExecState p sym ext rtp) ->@@ -109,28 +111,31 @@  pushFrame gvRef rebuildStack h mkSt st = stateSolverProof st $      do let sym = st^.stateSymInterface         let simCtx = st^.stateContext-        gv <- readIORef gvRef+        currGv <- readIORef gvRef+        let err = panic "pushFrame" ["Uninitialized global!"]+        let gv = fromMaybe err currGv         case lookupGlobal gv (st ^. stateGlobals) of           Nothing -> panic "bounded recursion" ["global not defined!"]           Just [] -> panic "bounded recursion" ["empty stack!"]-          Just (x:xs) ->-            do mb <- getRecursionBound h-               let v = 1 + fromMaybe 0 (Map.lookup h x)-               case mb of-                 Just b | v > b ->-                   do loc <- getCurrentProgramLoc sym-                      let msg = ("reached maximum number of recursive calls to function " ++ show h ++ " (" ++ show b ++ ")")-                      let err = SimError loc (ResourceExhausted msg)-                      when generateSideConditions $ withBackend simCtx $ \bak ->-                        addProofObligation bak (LabeledPred (falsePred sym) err)-                      return (ExecutionFeatureNewState (AbortState (AssertionFailure err) st))-                 _ ->-                   do let x'  = Map.insert h v x-                      let st' = st & stateGlobals %~ insertGlobal gv (rebuildStack x' x xs)-                      x' `seq` return (ExecutionFeatureModifiedState (mkSt st'))+          Just (x:xs) -> do +            mb <- getRecursionBound h+            let v = 1 + fromMaybe 0 (Map.lookup h x)+            case mb of+              Just b | v > b -> do +                loc <- getCurrentProgramLoc sym+                let msg = ("reached maximum number of recursive calls to function " ++ show h ++ " (" ++ show b ++ ")")+                let simerr = SimError loc (ResourceExhausted msg)+                when generateSideConditions $ withBackend simCtx $ \bak ->+                  addProofObligation bak (LabeledPred (falsePred sym) simerr)+                return (ExecutionFeatureNewState (AbortState (AssertionFailure simerr) st))+              _ -> do +                let x'  = Map.insert h v x+                let st' = st & stateGlobals %~ insertGlobal gv (rebuildStack x' x xs)+                x' `seq` return (ExecutionFeatureModifiedState (mkSt st')) +  onStep ::-   IORef BoundedRecursionGlobal ->+   IORef (Maybe BoundedRecursionGlobal) ->    ExecState p sym ext rtp ->    IO (ExecutionFeatureResult p sym ext rtp) @@ -138,8 +143,13 @@     InitialState simctx globals ah ret cont ->      do let halloc = simHandleAllocator simctx-        gv <- freshGlobalVar halloc (Text.pack "BoundedRecursionData") knownRepr-        writeIORef gvRef gv+        currGv <- readIORef gvRef +        gv <- case currGv of +          Just gv -> pure gv +          Nothing -> do+            gv <- freshGlobalVar halloc (Text.pack "BoundedRecursionData") knownRepr+            writeIORef gvRef (Just gv)+            pure gv         let simctx'  = simctx{ ctxIntrinsicTypes = MapF.insert                                    (knownSymbol @"BoundedRecursionData")                                    IntrinsicMuxFn
src/Lang/Crucible/Simulator/CallFrame.hs view
@@ -281,7 +281,13 @@   FrameRetType (CrucibleLang b r) = r   FrameRetType (OverrideLang r) = r -data SimFrame sym ext l (args :: Maybe (Ctx CrucibleType)) where+-- | A frame on the stack.+--+--   Type parameters:+--+--   - @f@: the type of the top frame ('CrucibleLang' or 'OverrideLang')+--   - @args@: arguments; 'Just' for call frames, 'Nothing' for a return frame+data SimFrame sym ext f (args :: Maybe (Ctx CrucibleType)) where   -- | Custom code to execute, typically for "overrides"   OF :: !(OverrideFrame sym ret args)      -> SimFrame sym ext (OverrideLang ret) ('Just args)
src/Lang/Crucible/Simulator/ExecutionTree.hs view
@@ -30,6 +30,7 @@ {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneKindSignatures #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# OPTIONS_GHC -fprint-explicit-kinds -Wall #-}@@ -68,8 +69,12 @@   , ResolvedCall(..)   , resolvedCallHandle   , execResultContext+  , setExecResultContext   , execStateContext+  , setExecStateContext   , execStateSimState+  , execResultGlobals+  , execStateGlobals      -- * Simulator context trees     -- ** Main context data structures@@ -174,8 +179,14 @@ ------------------------------------------------------------------------ -- GlobalPair --- | A value of some type 'v' together with a global state.-data GlobalPair sym (v :: Type) =+-- | A value of some type @v@ together with a global state.+--+--   Type parameters:+--+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @v@: type of the value+type GlobalPair :: Type -> Type -> Type+data GlobalPair sym v =    GlobalPair    { _gpValue :: !v    , _gpGlobals :: !(SymGlobalState sym)@@ -194,7 +205,15 @@ -- TopFrame  -- | The currently-executing frame plus the global state associated with it.-type TopFrame sym ext f a = GlobalPair sym (SimFrame sym ext f a)+--+--   Type parameters:+--+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+--   - @f@: the type of the top frame ('CrucibleLang' or 'OverrideLang')+--   - @args@: arguments to this frame (see 'SimFrame')+type TopFrame :: Type -> Type -> Type -> Maybe (Ctx CrucibleType) -> Type+type TopFrame sym ext f args = GlobalPair sym (SimFrame sym ext f args)  -- | Access the Crucible call frame inside a 'TopFrame'. crucibleTopFrame ::@@ -222,6 +241,12 @@ --   path might abort because it became infeasible (inconsistent path --   conditions), because the program called an exit primitive, or --   because of a true error condition (e.g., a failed assertion).+--+--   Type parameters:+--+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+type AbortedResult :: Type -> Type -> Type data AbortedResult sym ext where   -- | A single aborted execution with the execution state at time of the abort and the reason.   AbortedExec ::@@ -229,9 +254,10 @@     !(GlobalPair sym (SimFrame sym ext l args)) ->     AbortedResult sym ext -  -- | An aborted execution that was ended by a call to 'exit'.+  -- | An aborted execution that was ended by a call to @exit@.   AbortedExit ::     !ExitCode ->+    !(GlobalPair sym (SimFrame sym ext l args)) ->     AbortedResult sym ext    -- | Two separate threads of execution aborted after a symbolic branch,@@ -248,7 +274,10 @@  -- | This represents an execution frame where its frame type --   and arguments have been hidden.-data SomeFrame (f :: fk -> argk -> Type) = forall l a . SomeFrame !(f l a)+--+--   The type parameter @f@ is usually 'SimFrame'.+type SomeFrame :: forall fk argk. (fk -> argk -> Type) -> Type+data SomeFrame f = forall l a . SomeFrame !(f l a)  -- | Return the program locations of all the Crucible frames. filterCrucibleFrames :: SomeFrame (SimFrame sym ext) -> Maybe ProgramLoc@@ -260,7 +289,9 @@ arFrames h (AbortedExec e p) =   (\(SomeFrame f') -> AbortedExec e (p & gpValue .~ f'))      <$> h (SomeFrame (p^.gpValue))-arFrames _ (AbortedExit ec) = pure (AbortedExit ec)+arFrames h (AbortedExit ec p) =+  (\(SomeFrame f') -> AbortedExit ec (p & gpValue .~ f'))+     <$> h (SomeFrame (p^.gpValue)) arFrames h (AbortedBranch predicate loc r s) =   AbortedBranch predicate loc <$> arFrames h r                               <*> arFrames h s@@ -289,7 +320,14 @@ --   'PartialResult', then some of the computation paths that led to --   this result aborted for some reason, and the resulting value is --   only defined if the associated condition is true.-data PartialResult sym ext (v :: Type)+--+--   Type parameters:+--+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+--   - @v@: Type of the result of the computation+type PartialResult :: Type -> Type -> Type -> Type+data PartialResult sym ext v       {- | A 'TotalRes' indicates that the the global pair is always defined. -}    = TotalRes !(GlobalPair sym v)@@ -318,6 +356,14 @@ {-# INLINE partialValue #-}  -- | The result of resolving a function call.+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+--   - @ret@: 'CrucibleType' of the return value+type ResolvedCall :: Type -> Type -> Type -> CrucibleType -> Type data ResolvedCall p sym ext ret where   -- | A resolved function call to an override.   OverrideCall ::@@ -341,15 +387,23 @@  -- | Executions that have completed either due to (partial or total) --   successful completion or by some abort condition.-data ExecResult p sym ext (r :: Type)+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+--   - @rtp@: type of the return value+type ExecResult :: Type -> Type -> Type -> Type -> Type+data ExecResult p sym ext rtp    = -- | At least one execution path resulted in some return result.-     FinishedResult !(SimContext p sym ext) !(PartialResult sym ext r)+     FinishedResult !(SimContext p sym ext) !(PartialResult sym ext rtp)      -- | All execution paths resulted in an abort condition, and there is      --   no result to return.    | AbortedResult  !(SimContext p sym ext) !(AbortedResult sym ext)      -- | An execution stopped somewhere in the middle of a run because      --   a timeout condition occurred.-   | TimeoutResult !(ExecState p sym ext r)+   | TimeoutResult !(ExecState p sym ext rtp)   execResultContext :: ExecResult p sym ext r -> SimContext p sym ext@@ -357,6 +411,16 @@ execResultContext (AbortedResult ctx _) = ctx execResultContext (TimeoutResult exst) = execStateContext exst +setExecResultContext ::+  SimContext p sym ext ->+  ExecResult p sym ext r ->+  ExecResult p sym ext r+setExecResultContext ctx =+  \case+    FinishedResult _ x -> FinishedResult ctx x+    AbortedResult _ x -> AbortedResult ctx x+    TimeoutResult execState -> TimeoutResult (setExecStateContext ctx execState)+ execStateContext :: ExecState p sym ext r -> SimContext p sym ext execStateContext = \case   ResultState res        -> execResultContext res@@ -372,6 +436,24 @@   BranchMergeState _ st -> st^.stateContext   InitialState stctx _ _ _ _ -> stctx +setExecStateContext ::+  SimContext p sym ext ->+  ExecState p sym ext r ->+  ExecState p sym ext r+setExecStateContext ctx = \case+  ResultState res        -> ResultState (setExecResultContext ctx res)+  AbortState x st -> AbortState x (st & stateContext .~ ctx)+  UnwindCallState x y st -> UnwindCallState x y (st & stateContext .~ ctx)+  CallState x y st -> CallState x y (st & stateContext .~ ctx)+  TailCallState x y st -> TailCallState x y (st & stateContext .~ ctx)+  ReturnState x y z st -> ReturnState x y z (st & stateContext .~ ctx)+  ControlTransferState x st -> ControlTransferState x (st & stateContext .~ ctx)+  RunningState x st -> RunningState x (st & stateContext .~ ctx)+  SymbolicBranchState u v x y st -> SymbolicBranchState u v x y (st & stateContext .~ ctx)+  OverrideState x st -> OverrideState x (st & stateContext .~ ctx)+  BranchMergeState x st -> BranchMergeState x (st & stateContext .~ ctx)+  InitialState _ u v x y -> InitialState ctx u v x y+ execStateSimState :: ExecState p sym ext r                   -> Maybe (SomeSimState p sym ext r) execStateSimState = \case@@ -388,6 +470,66 @@   BranchMergeState _ st          -> Just (SomeSimState st)   InitialState _ _ _ _ _         -> Nothing +abortedGlobals ::+  Monad f =>+  -- | How to handle 'AbortedBranch'.+  --+  -- Common options include concretizing the 'Pred' or returning a partial+  -- result (e.g., 'Nothing').+  (ProgramLoc -> Pred sym -> SymGlobalState sym -> SymGlobalState sym -> f (SymGlobalState sym)) ->+  AbortedResult sym ext ->+  f (SymGlobalState sym)+abortedGlobals handleBranch =+  \case+    AbortedExec _ gp -> pure (gp ^. gpGlobals)+    AbortedExit _ gp -> pure (gp ^. gpGlobals)+    AbortedBranch loc p rl rr -> do+      l <- abortedGlobals handleBranch rl+      r <- abortedGlobals handleBranch rr+      handleBranch loc p l r++-- | Extract the 'SymGlobalState' from an 'ExecResult'.+execResultGlobals ::+  Monad f =>+  -- | How to handle 'AbortedBranch'.+  --+  -- Common options include concretizing the 'Pred' or returning a partial+  -- result (e.g., 'Nothing').+  (SimContext p sym ext -> ProgramLoc -> Pred sym -> SymGlobalState sym -> SymGlobalState sym -> f (SymGlobalState sym)) ->+  ExecResult p sym ext rtp ->+  f (SymGlobalState sym)+execResultGlobals handleBranch =+  \case+    FinishedResult _ctx partial -> pure (partial ^. partialValue . gpGlobals)+    TimeoutResult st -> execStateGlobals handleBranch st+    AbortedResult simCtx aborted ->+      abortedGlobals (handleBranch simCtx) aborted++-- | Extract the 'SymGlobalState' from an 'ExecState'.+execStateGlobals ::+  Monad f =>+  -- | How to handle 'AbortedBranch'.+  --+  -- Common options include concretizing the 'Pred' or returning a partial+  -- result (e.g., 'Nothing').+  (SimContext p sym ext -> ProgramLoc -> Pred sym -> SymGlobalState sym -> SymGlobalState sym -> f (SymGlobalState sym)) ->+  ExecState p sym ext rtp ->+  f (SymGlobalState sym)+execStateGlobals handleBranch =+  \case+    AbortState _ st -> pure (st ^. stateGlobals)+    BranchMergeState _ st -> pure (st ^. stateGlobals)+    CallState _ _ st -> pure (st ^. stateGlobals)+    ControlTransferState _ st -> pure (st ^. stateGlobals)+    InitialState _ globState _ _ _ -> pure globState+    OverrideState _ st -> pure (st ^. stateGlobals)+    ResultState r -> execResultGlobals handleBranch r+    ReturnState _ _ _ st -> pure (st ^. stateGlobals)+    RunningState _ st -> pure (st ^. stateGlobals)+    SymbolicBranchState _ _ _ _ st -> pure (st ^. stateGlobals)+    TailCallState _ _ st -> pure (st ^. stateGlobals)+    UnwindCallState _ _ st -> pure (st ^. stateGlobals)+ ----------------------------------------------------------------------- -- ExecState @@ -395,7 +537,15 @@ --   Crucible program.  The Crucible simulator executes by transitioning --   between these different states until it results in a 'ResultState', --   indicating the program has completed.-data ExecState p sym ext (rtp :: Type)+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+--   - @rtp@: type of the return value+type ExecState :: Type -> Type -> Type -> Type -> Type+data ExecState p sym ext rtp    {- | The 'ResultState' is used to indicate that the program has completed. -}    = ResultState        !(ExecResult p sym ext rtp)@@ -532,11 +682,27 @@ -- | An action which will construct an 'ExecState' given a current --   'SimState'. Such continuations correspond to a single transition --   of the simulator transition system.-type ExecCont p sym ext r f a =-  ReaderT (SimState p sym ext r f a) IO (ExecState p sym ext r)+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+--   - @rtp@: type of the return value+--   - @f@: the type of the top frame ('CrucibleLang' or 'OverrideLang')+--   - @args@: arguments to the current frame (see 'SimFrame')+type ExecCont :: Type -> Type -> Type -> Type -> Type -> Maybe (Ctx.Ctx CrucibleType) -> Type+type ExecCont p sym ext rtp f args =+  ReaderT (SimState p sym ext rtp f args) IO (ExecState p sym ext rtp)  -- | Some additional information attached to a @RunningState@ --   that indicates how we got to this running state.+--+--   Type parameters:+--+--   - @blocks@: types of variables in scope from previous blocks+--   - @args@: arguments to this block+type RunningStateInfo :: Ctx (Ctx CrucibleType) -> Ctx CrucibleType -> Type data RunningStateInfo blocks args     -- | This indicates that we are now in a @RunningState@ because     --   we transferred execution to the start of a basic block.@@ -554,6 +720,12 @@ -- | A 'ResolvedJump' is a block label together with a collection of --   actual arguments that are expected by that block.  These data --   are sufficient to actually transfer control to the named label.+--+--   Type parameters:+--+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @blocks@: types of variables in scope from previous blocks+type ResolvedJump :: Type -> Ctx (Ctx CrucibleType) -> Type data ResolvedJump sym blocks   = forall args.       ResolvedJump@@ -564,6 +736,15 @@ --   (while it first explores other paths), a 'ControlResumption' --   indicates what actions must later be taken in order to resume --   execution of that path.+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+--   - @rtp@: type of the return value+--   - @f@: the type of the top frame ('CrucibleLang' or 'OverrideLang')+type ControlResumption :: Type -> Type -> Type -> Type -> Type -> Type data ControlResumption p sym ext rtp f where   {- | When resuming a paused frame with a @ContinueResumption@,        no special work needs to be done, simply begin executing@@ -603,6 +784,15 @@ --   while other paths are explored.  It consists of a (potentially partial) --   'SimFrame' together with some information about how to resume execution --   of that frame.+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+--   - @rtp@: type of the return value+--   - @f@: the type of the top frame ('CrucibleLang' or 'OverrideLang')+type PausedFrame :: Type -> Type -> Type -> Type -> Type -> Type data PausedFrame p sym ext rtp f    = forall old_args.        PausedFrame@@ -619,6 +809,16 @@ --   stored in the 'VFFCompletePath' state until the second path also --   reaches its merge point.  The two paths will then be merged, --   and execution will continue beyond the merge point.+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+--   - @rtp@: type of the return value+--   - @f@: the type of the top frame ('CrucibleLang' or 'OverrideLang')+--   - @args@: arguments to this frame (see 'SimFrame')+type VFFOtherPath :: Type -> Type -> Type -> Type -> Type -> Maybe (Ctx CrucibleType) -> Type data VFFOtherPath p sym ext ret f args       {- | This corresponds the a path that still needs to be analyzed. -}@@ -639,20 +839,17 @@ of the branching structure of a program.  The 'ValueFromFrame' states correspond to the structure of symbolic branching that occurs within a single function call. -The type parameters have the following meanings:--  * @p@ is the personality of the simulator (i.e., custom user state).--  * @sym@ is the simulator backend being used.--  * @ext@ specifies what extensions to the Crucible language are enabled--  * @ret@ is the global return type of the entire execution.+Type parameters: -  * @f@ is the type of the top frame.+- @p@: see 'cruciblePersonality'+- @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+- @ext@: language extension, see "Lang.Crucible.CFG.Extension"+- @ret@: global return type of the entire execution+- @f@: the type of the top frame ('CrucibleLang' or 'OverrideLang') -} -data ValueFromFrame p sym ext (ret :: Type) (f :: Type)+type ValueFromFrame :: Type -> Type -> Type -> Type -> Type -> Type+data ValueFromFrame p sym ext ret f    {- | We are working on a branch;  this could be the first or the second        of both branches (see the 'VFFOtherPath' field). -}@@ -715,6 +912,7 @@ --   occur or not.  If the context sill expects a merge, we need to --   take some actions to indicate that the merge will not occur; --   otherwise there is no special work to be done.+type PendingPartialMerges :: Type data PendingPartialMerges =     {- | Don't indicate an abort condition in the context -}     NoNeedToAbort@@ -728,19 +926,14 @@ of the branching structure of a program.  The 'ValueFromValue' states correspond to stack call frames in a more traditional simulator environment. -The type parameters have the following meanings:--  * @p@ is the personality of the simulator (i.e., custom user state).--  * @sym@ is the simulator backend being used.--  * @ext@ specifies what extensions to the Crucible language are enabled--  * @ret@ is the global return type of the entire computation--  * @top_return@ is the return type of the top-most call on the stack.+- @p@: see 'cruciblePersonality'+- @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+- @ext@: language extension, see "Lang.Crucible.CFG.Extension"+- @ret@: global return type of the entire execution+- @top_return@: return type of the top-most call on the stack. -}-data ValueFromValue p sym ext (ret :: Type) (top_return :: CrucibleType)+type ValueFromValue :: Type -> Type -> Type -> Type -> CrucibleType -> Type+data ValueFromValue p sym ext ret top_return    {- | 'VFVCall' denotes a call site in the outer context, and represents        the point to which a function higher on the stack will@@ -851,23 +1044,18 @@ executing in a caller's context once a function call has completed and the return value is available. -The type parameters have the following meanings:--  * @ret@ is the type of the return value that is expected.--  * @p@ is the personality of the simulator (i.e., custom user state).--  * @sym@ is the simulator backend being used.--  * @ext@ specifies what extensions to the Crucible language are enabled.--  * @root@ is the global return type of the entire computation.--  * @f@ is the stack type of the caller.+Type parameters: -  * @args@ is the type of the local variables in scope prior to the call.+- @ret@: the type of the return value that is expected+- @p@: see 'cruciblePersonality'+- @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+- @ext@: language extension, see "Lang.Crucible.CFG.Extension"+- @root@: global return type of the entire computation+- @f@: the frame type of the caller ('CrucibleLang' or 'OverrideLang')+- @args@: types of the local variables in scope prior to the call (see 'SimFrame') -}-data ReturnHandler (ret :: CrucibleType) p sym ext root f args where+type ReturnHandler :: CrucibleType -> Type -> Type -> Type -> Type -> Type -> Maybe (Ctx CrucibleType) -> Type+data ReturnHandler ret p sym ext root f args where   {- | The 'ReturnToOverride' constructor indicates that the calling        context is primitive code written directly in Haskell.    -}@@ -899,13 +1087,25 @@ ------------------------------------------------------------------------ -- ActiveTree +type PartialResultFrame :: Type -> Type -> Type -> Maybe (Ctx CrucibleType) -> Type type PartialResultFrame sym ext f args =   PartialResult sym ext (SimFrame sym ext f args)  {- | An active execution tree contains at least one active execution.      The data structure is organized so that the current execution-     can be accessed rapidly. -}-data ActiveTree p sym ext root (f :: Type) args+     can be accessed rapidly.++     Type parameters:++     - @p@: see 'cruciblePersonality'+     - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+     - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+     - @root@: global return type of the entire computation+     - @f@: the frame type of the caller ('CrucibleLang' or 'OverrideLang')+--   - @args@: arguments to the current frame (see 'SimFrame')+-}+type ActiveTree :: Type -> Type -> Type -> Type -> Type -> Maybe (Ctx.Ctx CrucibleType) -> Type+data ActiveTree p sym ext root f args    = ActiveTree       { _actContext :: !(ValueFromFrame p sym ext root f)       , _actResult  :: !(PartialResultFrame sym ext f args)@@ -960,6 +1160,15 @@ -- SimContext  -- | A definition of a function's semantics, given as a Haskell action.+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+--   - @args@: types of arguments to the override+--   - @ret@: return type of the override+type Override :: Type -> Type -> Type -> Ctx CrucibleType -> CrucibleType -> Type data Override p sym ext (args :: Ctx CrucibleType) ret    = Override { overrideName    :: FunctionName               , overrideHandler :: forall r. ExecCont p sym ext r (OverrideLang ret) ('Just args)@@ -968,16 +1177,39 @@ -- | State used to indicate what to do when function is called.  A function --   may either be defined by writing a Haskell 'Override' or by giving --   a Crucible control-flow graph representation.-data FnState p sym ext (args :: Ctx CrucibleType) (ret :: CrucibleType)+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+--   - @args@: argument types+--   - @ret@: return type+type FnState :: Type -> Type -> Type -> Ctx CrucibleType -> CrucibleType -> Type+data FnState p sym ext args ret    = UseOverride !(Override p sym ext args ret)    | forall blocks . UseCFG !(CFG ext blocks args ret) !(CFGPostdom blocks)  -- | A map from function handles to their semantics.+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+type FunctionBindings :: Type -> Type -> Type -> Type newtype FunctionBindings p sym ext = FnBindings { fnBindings :: FnHandleMap (FnState p sym ext) }  -- | The type of functions that interpret extension statements.  These --   have access to the main simulator state, and can make fairly arbitrary --   changes to it.+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+type EvalStmtFunc :: Type -> Type -> Type -> Type type EvalStmtFunc p sym ext =   forall rtp blocks r ctx tp'.     StmtExtension ext (RegEntry sym) tp' ->@@ -987,6 +1219,13 @@ -- | In order to start executing a simulator, one must provide an implementation --   of the extension syntax.  This includes an evaluator for the added --   expression forms, and an evaluator for the added statement forms.+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+type ExtensionImpl :: Type -> Type -> Type -> Type data ExtensionImpl p sym ext   = ExtensionImpl     { extensionEval ::@@ -1010,8 +1249,17 @@   , extensionExec = \case   } +type IsSymInterfaceProof :: Type -> Type -> Type type IsSymInterfaceProof sym a = (IsSymInterface sym => a) -> a +-- | Some kind of 'Integer' to be collected during execution.+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+type Metric :: Type -> Type -> Type -> Type newtype Metric p sym ext =   Metric {     runMetric :: forall rtp f args. SimState p sym ext rtp f args -> IO Integer@@ -1021,7 +1269,14 @@ --   remains persistent across all symbolic simulator actions.  In particular, it --   is not rolled back when the simulator returns previous program points to --   explore additional paths, etc.-data SimContext (personality :: Type) (sym :: Type) (ext :: Type)+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+type SimContext :: Type -> Type -> Type -> Type+data SimContext p sym ext    = SimContext { _ctxBackend            :: !(SomeBackend sym)                   -- | Class dictionary for @'IsSymInterface' sym@                 , ctxSolverProof         :: !(forall a . IsSymInterfaceProof sym a)@@ -1030,10 +1285,11 @@                 , simHandleAllocator     :: !(HandleAllocator)                   -- | Handle to write messages to.                 , printHandle            :: !Handle-                , extensionImpl          :: ExtensionImpl personality sym ext-                , _functionBindings      :: !(FunctionBindings personality sym ext)-                , _cruciblePersonality   :: !personality-                , _profilingMetrics      :: !(Map Text (Metric personality sym ext))+                , extensionImpl          :: ExtensionImpl p sym ext+                , _functionBindings      :: !(FunctionBindings p sym ext)+                  -- | See 'cruciblePersonality'.+                , _cruciblePersonality   :: !p+                , _profilingMetrics      :: !(Map Text (Metric p sym ext))                 }  -- | Create a new 'SimContext' with the given bindings.@@ -1075,7 +1331,30 @@ functionBindings :: Lens' (SimContext p sym ext) (FunctionBindings p sym ext) functionBindings = lens _functionBindings (\s v -> s { _functionBindings = v }) --- | Access the custom user-state inside the 'SimContext'.+-- | Custom state inside the 'SimContext'.+--+-- Crucible itself is entirely polymorphic over @p@. Downstream applications can+-- instantiate it to any sort of state that they would like to associate with a+-- 'SimContext'.+--+-- For example, applications based on+-- [@macaw-symbolic@](https://github.com/GaloisInc/macaw/tree/master/symbolic)+-- can instantiate this to a structure holding enough information to perform+-- incremental code discovery. See @ambient-verifier@'s+-- [@AmbientSimulatorState@](https://github.com/GaloisInc/ambient-verifier/blob/eab04abb9750825a25ec0cbe0379add63f05f6c6/src/Ambient/Extensions.hs#L1092-1137).+--+-- Code that needs to store some state in the personality but doesn\'t wish to+-- fix a particular type can use the \"classy lenses\" approach, e.g.,+--+-- @+-- class HasFooState p where+--   fooState :: `Lens'` p FooState+-- @+--+-- For examples of this approach, see+--+-- * [@HasMacawLazySimulatorState@](https://github.com/GaloisInc/macaw/blob/cbec559b428fdd194398d07fc08c8c570a1d3bab/symbolic/src/Data/Macaw/Symbolic/MemOps.hs#L385-L394)+-- * [@HasGreaseSimulatorState@](https://github.com/GaloisInc/grease/blob/a50d54d2f414d15974dcf2d21654fbfe3527f0fa/src/Grease/Macaw/SimulatorState.hs#L90-L97) cruciblePersonality :: Lens' (SimContext p sym ext) p cruciblePersonality = lens _cruciblePersonality (\s v -> s{ _cruciblePersonality = v }) @@ -1094,6 +1373,13 @@ --   may be desirable to take additional or alternate actions on abort --   events; in which case, the library user may replace the default --   abort handler with their own.+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+--   - @rtp@: type of the return value newtype AbortHandler p sym ext rtp       = AH { runAH :: forall (l :: Type) args.                  AbortExecReason ->@@ -1103,12 +1389,23 @@ -- | A SimState contains the execution context, an error handler, and --   the current execution tree.  It captures the entire state --   of the symbolic simulator.-data SimState p sym ext rtp f (args :: Maybe (Ctx.Ctx CrucibleType))+--+--   Type parameters:+--+--   - @p@: see 'cruciblePersonality'+--   - @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+--   - @ext@: language extension, see "Lang.Crucible.CFG.Extension"+--   - @rtp@: type of the return value+--   - @f@: the type of the top frame ('CrucibleLang' or 'OverrideLang')+--   - @args@: arguments to the current frame (see 'SimFrame')+type SimState :: Type -> Type -> Type -> Type -> Type -> Maybe (Ctx.Ctx CrucibleType) -> Type+data SimState p sym ext rtp f args    = SimState { _stateContext      :: !(SimContext p sym ext)               , _abortHandler      :: !(AbortHandler p sym ext rtp)               , _stateTree         :: !(ActiveTree p sym ext rtp f args)               } +type SomeSimState :: Type -> Type -> Type -> Type -> Type data SomeSimState p sym ext rtp =   forall f args. SomeSimState !(SimState p sym ext rtp f args) 
src/Lang/Crucible/Simulator/Operations.hs view
@@ -125,8 +125,8 @@   AbortedResult sym ext ->   AbortedResult sym ext ->   AbortedResult sym ext-mergeAbortedResult _ _ (AbortedExit ec) _ = AbortedExit ec-mergeAbortedResult _ _ _ (AbortedExit ec) = AbortedExit ec+mergeAbortedResult _ _ ae@(AbortedExit {}) _ = ae+mergeAbortedResult _ _ _ ae@(AbortedExit {}) = ae mergeAbortedResult loc pred q r = AbortedBranch loc pred q r  mergePartialAndAbortedResult ::
src/Lang/Crucible/Simulator/OverrideSim.hs view
@@ -75,8 +75,10 @@   , useIntrinsic     -- * Typed overrides   , TypedOverride(..)+  , typedOverride   , SomeTypedOverride(..)   , runTypedOverride+  , bindTypedOverride     -- * Re-exports   , Lang.Crucible.Simulator.ExecutionTree.Override   ) where@@ -89,7 +91,7 @@ import           Control.Monad.Reader (ReaderT(..)) import           Control.Monad.ST import           Control.Monad.State.Strict (StateT(..))-import           Data.List (foldl')+import qualified Data.Foldable as Foldable import qualified Data.Parameterized.Context as Ctx import           Data.Proxy import qualified Data.Text as T@@ -135,7 +137,7 @@ -- -- Type parameters: -----   * 'p'    the "personality", i.e. user-defined state parameterized by @sym@+--   * 'p'    see 'Lang.Crucible.Simulator.ExecutionTree.cruciblePersonality' --   * 'sym'  the symbolic backend --   * 'ext'  the syntax extension ("Lang.Crucible.CFG.Extension") --   * 'rtp'  global return type@@ -156,8 +158,14 @@ -- | Exit from the current execution by ignoring the continuation --   and immediately returning an aborted execution result. exitExecution :: IsSymInterface sym => ExitCode -> OverrideSim p sym ext rtp args r a-exitExecution ec = Sim $ StateContT $ \_c s ->-  return $ ResultState $ AbortedResult (s^.stateContext) (AbortedExit ec)+exitExecution ec = do+  ActiveTree _ctx ar0 <- use stateTree+  let gp =+        case ar0 of+          TotalRes e -> e+          PartialRes _loc _pred ex _ar1 -> ex+  Sim $ StateContT $ \_c s ->+    return $ ResultState $ AbortedResult (s^.stateContext) (AbortedExit ec gp)  bindOverrideSim ::   OverrideSim p sym ext rtp args r a ->@@ -627,7 +635,7 @@ -- | Build a map of function bindings from a list of --   handle/binding pairs. fnBindingsFromList :: [FnBinding p sym ext] -> FunctionBindings p sym ext-fnBindingsFromList = foldl' insertFnBinding $ FnBindings emptyHandleMap+fnBindingsFromList = Foldable.foldl' insertFnBinding $ FnBindings emptyHandleMap  registerFnBinding :: FnBinding p sym ext                    -> OverrideSim p sym ext rtp a r ()@@ -690,6 +698,21 @@     , typedOverrideRet :: TypeRepr ret     } +-- | Create a 'TypedOverride' with a statically-known signature+typedOverride ::+  KnownRepr (Ctx.Assignment TypeRepr) args =>+  KnownRepr TypeRepr ret =>+  (forall rtp args' ret'.+    Ctx.Assignment (RegValue' sym) args ->+    OverrideSim p sym ext rtp args' ret' (RegValue sym ret)) ->+  TypedOverride p sym ext args ret+typedOverride handler =+  TypedOverride+  { typedOverrideHandler = handler+  , typedOverrideArgs = knownRepr+  , typedOverrideRet = knownRepr+  }+ -- | A 'TypedOverride' with the type parameters @args@, @ret@ existentially -- quantified data SomeTypedOverride p sym ext =@@ -703,3 +726,11 @@ runTypedOverride nm typedOvr = mkOverride' nm (typedOverrideRet typedOvr) $ do   RegMap args <- getOverrideArgs   typedOverrideHandler typedOvr (fmapFC (RV . regValue) args)++-- | Bind a 'TypedOverride' to a 'FnHandle'+bindTypedOverride ::+  FnHandle args ret ->+  TypedOverride p sym ext args ret ->+  OverrideSim p sym ext rtp args' ret' ()+bindTypedOverride hdl ov =+  bindFnHandle hdl (UseOverride (runTypedOverride (handleName hdl) ov))
src/Lang/Crucible/Simulator/PathSatisfiability.hs view
@@ -5,7 +5,7 @@ --                    at symbolic branch points -- Copyright        : (c) Galois, Inc 2018 -- License          : BSD3--- Maintainer       : Rob Dockins <rdockins@galois.com>+-- Maintainer       : Ryan Scott <rscott@galois.com>, Langston Barrett <langston@galois.com> -- Stability        : provisional ------------------------------------------------------------------------ {-# LANGUAGE DataKinds #-}@@ -54,14 +54,20 @@       (Just (ConcreteBool True))   ] -+-- | Prune unsatisfiable execution traces during simulation.+--+-- At every symbolic branch point, an SMT solver is queried to determine if one+-- or both symbolic branches are unsatisfiable. Only branches with satisfiable+-- branch conditions are explored. pathSatisfiabilityFeature :: forall sym.   IsSymInterface sym =>   sym ->-  (Maybe ProgramLoc -> Pred sym -> IO BranchResult)-   {- ^ An action for considering the satisfiability of a predicate.-        In the current state of the symbolic interface, indicate what-        we can determine about the given predicate. -} ->+  -- | An action for considering the satisfiability of a predicate. In the+  -- current state of the symbolic interface, indicate what we can determine+  -- about the given predicate.+  --+  -- Usually, this is set to 'Lang.Crucible.Backend.Online.considerSatisfiability'.+  (Maybe ProgramLoc -> Pred sym -> IO BranchResult) ->   IO (GenericExecutionFeature sym) pathSatisfiabilityFeature sym considerSatisfiability =   do tryExtendConfig pathSatOptions (getConfiguration sym)
+ src/Lang/Crucible/Simulator/RecordAndReplay.hs view
@@ -0,0 +1,365 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE StandaloneKindSignatures #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}++module Lang.Crucible.Simulator.RecordAndReplay (+  HasRecordState(..),+  RecordState,+  mkRecordState,+  HasReplayState(..),+  ReplayState,+  mkReplayState,+  recordTraceLength,+  replayTraceLength,+  RecordedTrace,+  getRecordedTrace,+  recordFeature,+  replayFeature,+  initialTrace,+  traceGlobal,+  emptyRecordedTrace+) where++import Control.Exception qualified as X+import Control.Lens ((%~), (&), (^.))+import Control.Lens qualified as Lens+import Data.Foldable qualified as F+import Data.Kind (Type)+import Data.Text qualified as Text+import Data.Sequence qualified as Seq+import Lang.Crucible.Backend qualified as CB+import Lang.Crucible.CFG.Core qualified as C+import Lang.Crucible.FunctionHandle qualified as C+import Lang.Crucible.Panic (panic)+import Lang.Crucible.Simulator qualified as C+import Lang.Crucible.Simulator.EvalStmt qualified as C+import Lang.Crucible.Simulator.ExecutionTree qualified as C+import Lang.Crucible.Simulator.GlobalState qualified as C+import Lang.Crucible.Simulator.SymSequence qualified as CSSS+import Lang.Crucible.Types qualified as CT+import What4.Interface qualified as W4+import What4.Partial qualified as W4P++-- | A trace consists of the 'W4.ProgramLoc's returned by+-- 'W4.getCurrentProgramLoc' in 'C.RunningState's during symbolic execution.+--+-- Intentionally not part of the API so as to keep the implementation abstract.+type TraceType = CT.SequenceType (CT.StringType W4.Unicode)++-- | Type parameters:+--+-- * @p@: see 'C.cruciblePersonality'+-- * @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+-- * @ext@: language extension, see "Lang.Crucible.CFG.Extension"+-- * @rtp@: type of the simulator return value+type RecordState :: Type -> Type -> Type -> Type -> Type+newtype RecordState p sym ext rtp+  = RecordState (C.GlobalVar TraceType)+    -- ^ constructor intentionally not exported++{- | A trace from 'recordFeature', processed and ready for consumption by+'replayFeature'.+-}+newtype RecordedTrace sym+  = RecordedTrace (C.RegValue sym TraceType)++-- | Type parameters:+--+-- * @p@: see 'C.cruciblePersonality'+-- * @sym@: instance of 'Lang.Crucible.Backend.IsSymInterface'+-- * @ext@: language extension, see "Lang.Crucible.CFG.Extension"+-- * @rtp@: type of the simulator return value+type ReplayState :: Type -> Type -> Type -> Type -> Type+data ReplayState p sym ext rtp+  = ReplayState+    { _traceGlobal :: (C.GlobalVar TraceType)+    , _initialTrace :: (RecordedTrace sym)+    }+    -- ^ constructor intentionally not exported+Lens.makeLenses ''ReplayState++-- | Constructor for 'RecordState'+mkRecordState ::+  C.HandleAllocator -> IO (RecordState p sym ext rtp)+mkRecordState halloc =+  RecordState <$> C.freshGlobalVar halloc "recordState" W4.knownRepr++-- | Constructor for 'ReplayState'+mkReplayState ::+  C.HandleAllocator -> RecordedTrace sym  -> IO (ReplayState p sym ext rtp)+mkReplayState halloc rt =+  ReplayState <$> C.freshGlobalVar halloc "replayState" W4.knownRepr <*> pure rt++-- | A class for Crucible personality types @p@ which contain a+-- 'RecordState'. This execution feature is polymorphic over+-- 'RecordState' so that downstream users can supply their own+-- personality types that extend 'RecordState' further.+class HasRecordState p r sym ext rtp | p -> r sym ext rtp where+  recordState :: Lens.Lens' p (RecordState r sym ext rtp)++instance HasRecordState (RecordState p sym ext rtp) p sym ext rtp where+  recordState = id+  {-# INLINE recordState #-}++-- | A class for Crucible personality types @p@ which contain a+-- 'ReplayState'. This execution feature is polymorphic over+-- 'ReplayState' so that downstream users can supply their own+-- personality types that extend 'ReplayState' further.+class HasReplayState p r sym ext rtp | p -> r sym ext rtp where+  replayState :: Lens.Lens' p (ReplayState r sym ext rtp)++instance HasReplayState (ReplayState p sym ext rtp) p sym ext rtp where+  replayState = id+  {-# INLINE replayState #-}++data TraceGlobalNotDefined = TraceGlobalNotDefined++instance Show TraceGlobalNotDefined where+  show _ = "record and replay trace global not defined"++instance X.Exception TraceGlobalNotDefined++locAsStr ::+  W4.IsExprBuilder sym =>+  sym ->+  IO (C.RegValue sym (CT.StringType W4.Unicode))+locAsStr sym = do+  loc <- W4.getCurrentProgramLoc sym+  let txtLoc = Text.pack (show loc)+  W4.stringLit sym (W4.UnicodeLiteral txtLoc)++emptyRecordedTrace :: sym -> IO (RecordedTrace sym)+emptyRecordedTrace sym = RecordedTrace <$> CSSS.nilSymSequence sym++getRecordTrace ::+  HasRecordState p p sym ext rtp =>+  C.SimState p sym ext rtp f args ->+  Maybe (C.RegValue sym TraceType)+getRecordTrace simState = do+  let ctx = simState ^. C.stateContext+  let RecordState g = ctx ^. C.cruciblePersonality . recordState+  C.lookupGlobal g (simState ^. C.stateGlobals)++-- | Get the length of the currently recorded trace+recordTraceLength ::+  W4.IsExprBuilder sym =>+  HasRecordState p p sym ext rtp =>+  C.SimState p sym ext rtp f args ->+  IO (Maybe (W4.SymNat sym))+recordTraceLength simState = do+  let sym = simState ^. C.stateSymInterface+  case getRecordTrace simState of+    Nothing -> pure Nothing+    Just s -> Just <$> CSSS.lengthSymSequence sym s++getReplayTrace ::+  HasReplayState p p sym ext rtp =>+  C.SimState p sym ext rtp f args ->+  Maybe (C.RegValue sym TraceType)+getReplayTrace simState = do+  let ctx = simState ^. C.stateContext+  let g = ctx ^. C.cruciblePersonality . replayState . traceGlobal+  C.lookupGlobal g (simState ^. C.stateGlobals)++-- | Get the length of the trace being replayed+replayTraceLength ::+  W4.IsExprBuilder sym =>+  HasReplayState p p sym ext rtp =>+  C.SimState p sym ext rtp f args ->+  IO (Maybe (W4.SymNat sym))+replayTraceLength simState = do+  let sym = simState ^. C.stateSymInterface+  case getReplayTrace simState of+    Nothing -> pure Nothing+    Just s -> Just <$> CSSS.lengthSymSequence sym s++-- | An 'C.ExecutionFeature' to record traces.+--+-- During execution this logs program locations to a Crucible global variable.+-- After execution, this variable may be read with 'getRecordedTrace' and the+-- 'RecordedTrace' can be passed to 'replayFeature' to \"replay\" it, i.e., to+-- abort all branches that deviate from it.+--+-- If this is not called with 'C.InitialState' before any other 'C.ExecState',+-- it may throw a 'TraceGlobalNotDefined' exception.+recordFeature ::+  ( HasRecordState p p sym ext rtp+  , W4.IsExprBuilder sym+  ) =>+  C.ExecutionFeature p sym ext rtp+recordFeature =+  C.ExecutionFeature $+    \case+      C.InitialState simCtx globals abortHandler retTy cont -> do+        globals' <- insertNewTrace simCtx globals+        let iState = C.InitialState simCtx globals' abortHandler retTy cont+        return $ C.ExecutionFeatureModifiedState iState+      C.RunningState runStateInfo st -> do+        loc <- locAsStr (st ^. C.stateSymInterface)+        st' <- consTrace st loc+        let rState = C.RunningState runStateInfo st'+        return $ C.ExecutionFeatureModifiedState rState+      _ -> pure C.ExecutionFeatureNoChange+  where+    insertNewTrace ::+      HasRecordState p p sym ext rtp =>+      C.SimContext p sym ext ->+      C.SymGlobalState sym ->+      IO (C.SymGlobalState sym)+    insertNewTrace simCtx globals = do+      let RecordState g = simCtx ^. C.cruciblePersonality . recordState+      let sym = simCtx ^. C.ctxSymInterface+      nil <- CSSS.nilSymSequence sym+      return (C.insertGlobal g nil globals)++    getTraceOrThrow ::+      HasRecordState p p sym ext rtp =>+      C.SimState p sym ext rtp f args ->+      IO (C.RegValue sym TraceType)+    getTraceOrThrow st =+      case getRecordTrace st of+        Nothing -> X.throw TraceGlobalNotDefined+        Just t -> pure t++    insertTrace ::+      HasRecordState p p sym ext rtp =>+      C.SimState p sym ext rtp f args ->+      C.RegValue sym TraceType ->+      C.SimState p sym ext rtp f args+    insertTrace st v = do+      let simCtx = st ^. C.stateContext+      let RecordState g = simCtx ^. C.cruciblePersonality . recordState+      st & C.stateGlobals %~ C.insertGlobal g v++    consTrace ::+      HasRecordState p p sym ext rtp =>+      C.SimState p sym ext rtp f args ->+      C.RegValue sym (CT.StringType W4.Unicode) ->+      IO (C.SimState p sym ext rtp f args)+    consTrace st v = do+      s <- getTraceOrThrow st+      let sym = st ^. C.stateSymInterface+      s' <- CSSS.consSymSequence sym v s+      pure (insertTrace st s')+++    -- ^ constructor intentionally not exported to keep 'TraceType' out of the+    -- API, but it could be exported in the future if necessary.++-- | Obtain a 'RecordedTrace' after execution.+--+-- This currently requires concretizing the trace, because there is no efficient+-- reverse operation for 'CSSS.SymSequence'.+getRecordedTrace ::+  W4.IsExprBuilder sym =>+  C.SymGlobalState sym ->+  RecordState p sym ext rtp ->+  sym ->+  -- | Evaluation for booleans, usually a 'What4.Expr.GroundEval.GroundEvalFn'+  (W4.Pred sym -> IO Bool) ->+  IO (RecordedTrace sym)+getRecordedTrace globals (RecordState g) sym evalBool = do+  case C.lookupGlobal g globals of+    Nothing -> X.throw TraceGlobalNotDefined+    Just s -> RecordedTrace <$> concretizeAndReverseTrace s+  where+    concretizeAndReverseTrace s = do+      concretized <- CSSS.concretizeSymSequence evalBool (evalStr sym) s+      let reversed = Seq.reverse concretized+      symbolized <- mapM (W4.stringLit sym . W4.UnicodeLiteral) reversed+      CSSS.fromListSymSequence sym (F.toList symbolized)++    evalStr ::+      W4.IsExpr (W4.SymExpr sym) =>+      sym ->+      W4.SymString sym W4.Unicode ->+      IO Text.Text+    evalStr _sym s =+      case W4.asString s of+        Just (W4.UnicodeLiteral s') -> pure s'+        Nothing -> panic "getRecordedTrace" ["Non-literal trace element?"]++{- | Inserts a recorded trace into the state's replay trace variable+The replay feature will follow this trace if it is enabled+-}+insertReplayTrace ::+  (HasReplayState p p sym ext rtp) =>+  C.SimState p sym ext rtp f args ->+  C.RegValue sym TraceType ->+  C.SimState p sym ext rtp f args+insertReplayTrace st v = do+  let simCtx = st ^. C.stateContext+  let g = simCtx ^. C.cruciblePersonality . replayState . traceGlobal+  st & C.stateGlobals %~ C.insertGlobal g v++-- | An 'C.ExecutionFeature' to replay traces recorded with 'recordFeature'.+--+-- Branches that deviate from the given trace will be aborted with+-- 'C.InfeasibleBranch'.+--+-- If this is not called with 'C.InitialState' before any other 'C.ExecState',+-- it may throw a 'TraceGlobalNotDefined' exception.+replayFeature ::+  ( HasReplayState p p sym ext rtp+  , W4.IsExprBuilder sym+  ) =>+  -- | Whether to stop at the end of the trace. If this is 'True' and execution+  -- has exhausted the trace, then any further execution will be aborted via+  -- 'C.InfeasibleBranch'.+  Bool ->+  C.ExecutionFeature p sym ext rtp+replayFeature stop =+  C.ExecutionFeature $+    \case+      C.InitialState simCtx globals abortHandler retTy cont -> do+        let rstate = simCtx ^. C.cruciblePersonality . replayState+        let g =  rstate ^. traceGlobal+        let RecordedTrace trace = rstate ^. initialTrace+        let globals' = C.insertGlobal g trace globals+        let iState = C.InitialState simCtx globals' abortHandler retTy cont+        return $ C.ExecutionFeatureModifiedState iState+      C.RunningState runStateInfo st -> do+        let sym = st ^. C.stateSymInterface+        s <- getTraceOrThrow st+        partExpr <- CSSS.unconsSymSequence sym (W4.stringIte sym) s+        let badPath = do+              loc <- W4.getCurrentProgramLoc sym+              let st' = C.AbortState (CB.InfeasibleBranch loc) st+              pure (C.ExecutionFeatureNewState st')+        case partExpr of+          W4P.Unassigned+            | stop -> badPath+            | otherwise -> pure C.ExecutionFeatureNoChange+          W4P.PE valid (expectedLoc, rest) ->+            C.withBackend (st ^. C.stateContext) $ \bak -> do+              let msg = "Trace must be valid"+              CB.assert bak valid (C.AssertFailureSimError msg "")++              currLoc <- locAsStr sym+              atExpectedLoc <- W4.stringEq sym currLoc expectedLoc+              case W4.asConstantPred atExpectedLoc of+                Just False -> badPath+                _ -> do+                  let msg' = "Execution deviated from trace"+                  CB.assert bak atExpectedLoc (C.AssertFailureSimError msg' "")+                  let st' = insertReplayTrace st rest+                  let rState = C.RunningState runStateInfo st'+                  pure (C.ExecutionFeatureModifiedState rState)++      _ -> pure C.ExecutionFeatureNoChange+  where+    getTraceOrThrow ::+      HasReplayState p p sym ext rtp =>+      C.SimState p sym ext rtp f args ->+      IO (C.RegValue sym TraceType)+    getTraceOrThrow st =+      case getReplayTrace st of+        Nothing -> X.throw TraceGlobalNotDefined+        Just t -> pure t
src/Lang/Crucible/Simulator/RegValue.hs view
@@ -48,6 +48,8 @@   , muxVector   , muxSymSequence   , muxHandle+    -- * Equality+  , eqRegValue   ) where  import           Control.Monad@@ -361,3 +363,41 @@                 p                 (unVB (x Ctx.! i))                 (unVB (y Ctx.! i))++------------------------------------------------------------------------+-- Equality++-- | Equality of 'RegValue's.+--+-- This is only supported for a few types, see #1582.+eqRegValue ::+  forall sym tp.+  IsInterpretedFloatExprBuilder sym =>+  sym ->+  TypeRepr tp ->+  RegValue sym tp ->+  RegValue sym tp ->+  IO (Pred sym)+eqRegValue sym tp x y =+  case tp of+    -- Base types+    BoolRepr -> eqPred sym x y+    BVRepr _width -> bvEq sym x y+    ComplexRealRepr -> cplxEq sym x y+    FloatRepr @fi _ -> iFloatEq @_ @fi sym x y+    IEEEFloatRepr _fpp -> floatEq sym x y+    IntegerRepr -> intEq sym x y+    NatRepr -> natEq sym x y+    RealValRepr -> realEq sym x y+    SymbolicStructRepr _tys -> structEq sym x y+    SymbolicArrayRepr _idxs _tp -> arrayEq sym x y+    StringRepr _si -> stringEq sym x y++    -- Trivial cases+    UnitRepr -> pure (truePred sym)+    CharRepr ->+      if x == y+      then pure (truePred sym)+      else pure (falsePred sym)++    _ -> fail ("eqRegValue not supported for " ++ show tp)
src/Lang/Crucible/Utils/BitSet.hs view
@@ -35,7 +35,7 @@ import Data.Word import Data.Hashable import qualified Data.List as List-import Prelude hiding (null, foldr, foldl)+import Prelude hiding (null, foldr, foldl, foldl')  newtype BitSet a = BitSet { getBits :: Integer }  deriving (Show, Eq, Ord)
test/helpers/Main.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE AllowAmbiguousTypes #-}-module Main where +module Main (main) where+ import Control.Lens ((^.)) import Data.List (isInfixOf) import Data.Maybe (fromMaybe)@@ -26,9 +27,12 @@  import qualified Panic as P +import qualified SymSequence as S+ main :: IO ()-main =-  defaultMain =<< panicTests+main = do+  p <- panicTests+  defaultMain (testGroup "crucible" [p, backendTests, S.tests])  mkBackend :: IO (Some SomeBackend) mkBackend = do@@ -81,7 +85,7 @@       c <- W4I.freshConstant sym (W4I.safeSymbol "c") W4I.BaseBoolRepr       assumePred bak "assuming c" c       d <- W4I.freshConstant sym (W4I.safeSymbol "d") W4I.BaseBoolRepr-      LCB.assert bak c (GenericSimError "asserting d")+      LCB.assert bak d (GenericSimError "asserting d")       (_asmps, mbGoals) <- LCB.popAssumptionFrameAndObligations bak frm       [LCB.ProofGoal asmps gl] <- pure (fromMaybe [] (LCB.goalsToList <$> mbGoals))       asmpsPred <- LCB.assumptionsPred sym asmps
+ test/helpers/SymSequence.hs view
@@ -0,0 +1,300 @@+{-# LANGUAGE EmptyDataDeriving #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ImportQualifiedPost #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}++module SymSequence (tests) where++import Control.Monad.IO.Class (liftIO)+import Data.Foldable qualified as F+import Data.List qualified as List+import Data.Maybe qualified as Maybe+import Data.Parameterized.Nonce qualified as Nonce+import Data.Parameterized.Some (Some(Some))+import Hedgehog (Gen)+import Hedgehog qualified as H+import Hedgehog.Gen qualified as Gen+import Hedgehog.Range qualified as Range+import Lang.Crucible.Backend (SomeBackend(SomeBackend), backendGetSym)+import Lang.Crucible.Backend.Simple (newSimpleBackend)+import Lang.Crucible.Simulator.SymSequence (SymSequence)+import Lang.Crucible.Simulator.SymSequence qualified as S+import Test.Tasty qualified as TT+import Test.Tasty.Hedgehog qualified as TTH+import What4.Expr (EmptyExprBuilderState(EmptyExprBuilderState))+import What4.Expr.Builder (newExprBuilder)+import What4.FloatMode (FloatModeRepr(FloatIEEERepr))+import What4.Interface qualified as WI+import What4.Partial qualified as WP++---------------------------------------------------------------------+-- Tests++tests :: TT.TestTree+tests =+  TTH.testProperty+    "propSame"+    -- This is a big API, so we want adequate coverage (default is 100)+    (H.withTests 4096 propSame)++-- | Check that a generated API interaction has the same effect when interpreted+-- with either 'SymSequence' or lists.+propSame :: H.Property+propSame =+  H.property $ do+    Some (SomeBackend bak) <- liftIO mkBackend+    let sym = backendGetSym bak+    op <- H.forAll (Gen.sized $ \n -> genList (H.unSize n) Gen.bool)+    let l = opList op+    s <- liftIO (opSeq sym op)+    l' <- liftIO (F.toList <$> asSeq sym s)+    l H.=== l'+  where+    asSeq sym =+      S.concretizeSymSequence (pure . asConstPred (Just sym)) pure++---------------------------------------------------------------------+-- Helpers++mkBackend :: IO (Some SomeBackend)+mkBackend = do+  sym <- newExprBuilder FloatIEEERepr EmptyExprBuilderState Nonce.globalNonceGenerator+  Some . SomeBackend <$> newSimpleBackend sym++-- Requires that the predicate is concrete+asConstPred ::+  WI.IsExprBuilder sym =>+  proxy sym ->+  WI.Pred sym ->+  Bool+asConstPred _proxy p =+  case WI.asConstantPred p of+    Just True -> True+    Just False -> False+    Nothing -> error "non-constant predicate?"++---------------------------------------------------------------------+-- Op++data Elem a deriving Show++data List a deriving Show++-- TODO: Replace with `Seq` for performance+type family AsList t where+  AsList (List a) = [a]+  AsList (Elem a) = a+  AsList (Maybe a) = Maybe (AsList a)+  AsList (a, b) = (AsList a, AsList b)+  AsList a = a++type family AsSeq sym t where+  AsSeq sym (List a) = SymSequence sym a+  AsSeq sym (Elem a) = a+  AsSeq sym (Maybe a) = Maybe (AsSeq sym a)+  AsSeq sym (a, b) = (AsSeq sym a, AsSeq sym b)+  AsSeq sym a = a++-- | An interaction with the 'SymSequence' API+data Op a t where+  -- Generic functions+  OTrue :: Op a Bool+  OFalse :: Op a Bool+  OFst :: Op a (l, r) -> Op a l+  OSnd :: Op a (l, r) -> Op a r+  OElem :: a -> Op a (Elem a)+  OFromMaybe :: Op a t -> Op a (Maybe t) -> Op a t++  -- Constructors+  ONil :: Op a (List a)+  OCons :: Op a (Elem a) -> Op a (List a) -> Op a (List a)+  OAppend :: Op a (List a) -> Op a (List a) -> Op a (List a)+  OMux :: Op a Bool -> Op a (List a) -> Op a (List a) -> Op a (List a)++  -- Operations+  OUncons :: Op a (List a) -> Op a (Maybe (Elem a), (List a))+  OLength :: Op a (List a) -> Op a Integer+  -- TODO: isNil, head, tail++sexp :: [String] -> String+sexp s = '(' : (unwords s ++ ")")++fun :: String -> [String] -> String+fun f s = sexp (f:s)++fun1 :: Show a => String -> a -> String+fun1 f a = fun f [show a]++fun2 :: (Show a, Show b) => String -> a -> b -> String+fun2 f a b = fun f [show a, show b]++fun3 :: (Show a, Show b, Show c) => String -> a -> b -> c -> String+fun3 f a b c = fun f [show a, show b, show c]++instance Show a => Show (Op a t) where+  show =+    \case+      -- Generic functions+      OTrue -> "true"+      OFalse -> "false"+      OFst t -> fun1 "fst" t+      OSnd t -> fun1 "snd" t+      OElem a -> show a+      OFromMaybe a m -> fun2 "fromMaybe" a m++      -- Constructors+      ONil -> "nil"+      OCons l r -> fun2 "cons" l r+      OAppend l r -> fun2 "append" l r+      OMux b l r -> fun3 "mux" b l r++      -- Operations+      OUncons l -> fun1 "uncons" l+      OLength l -> fun1 "length" l++---------------------------------------------------------------------+-- Generating Op++genBool :: Gen (Op a Bool)+genBool =+  Gen.choice+  [ pure OTrue+  , pure OFalse+  ]++genElem ::+  Int ->+  Gen a ->+  Gen (Op a (Elem a))+genElem sz genA =+  if sz <= 0+  then OElem <$> genA+  else+    Gen.choice+    [ OElem <$> genA+    , OFromMaybe+      <$> genElem (sz - 1) genA+      <*> (OFst <$> (OUncons <$> genList (sz - 1) genA))+    ]++genList ::+  Int ->+  Gen a ->+  Gen (Op a (List a))+genList sz genA =+  if sz <= 0+  then pure ONil+  else+    Gen.choice+    [ genCons+    , genAppend+    , genMux+    ]+  where+    sub1 = genList (sz - 1) genA+    sub2 = do+      let budget = max 0 (sz - 1)+      bl <- Gen.integral (Range.linear 0 budget)+      let br = max 0 (budget - bl)+      l <- genList bl genA+      r <- genList br genA+      pure (l, r)++    genCons = OCons <$> genElem (sz - 1) genA <*> sub1++    genAppend = uncurry OAppend <$> sub2++    genMux = do+      b <- genBool+      uncurry (OMux b) <$> sub2++---------------------------------------------------------------------+-- Interpreting Op++opList :: Op a t -> AsList t+opList =+  \case+    -- Generic functions+    OTrue -> True+    OFalse -> False+    OFst t -> fst (opList t)+    OSnd t -> snd (opList t)+    OElem a -> a+    OFromMaybe a m -> Maybe.fromMaybe (opList a) (opList m)++    -- Constructors+    ONil -> []+    OCons a l -> opList a : opList l+    OAppend l r -> opList l ++ opList r+    OMux b l r -> if opList b then opList l else opList r++    -- Operations+    OUncons l ->+      let l' = opList l in+      case List.uncons l' of+        Just (hd, tl) -> (Just hd, tl)+        Nothing -> (Nothing, l')+    OLength l -> fromIntegral @Int @Integer (length (opList l))  -- safe++opSeq ::+  WI.IsExprBuilder sym =>+  sym ->+  Op a t ->+  IO (AsSeq sym t)+opSeq sym =+  \case+    -- Generic functions+    OTrue -> pure True+    OFalse -> pure False+    OFst t -> fst <$> opSeq sym t+    OSnd t -> snd <$> opSeq sym t+    OElem a -> pure a+    OFromMaybe a m ->+      Maybe.fromMaybe+      <$> opSeq sym a+      <*> opSeq sym m++    -- Constructors+    ONil -> pure S.SymSequenceNil+    OCons a l ->+      S.SymSequenceCons+      <$> Nonce.freshNonce Nonce.globalNonceGenerator+      <*> opSeq sym a+      <*> opSeq sym l+    OAppend l r ->+      S.SymSequenceAppend+      <$> Nonce.freshNonce Nonce.globalNonceGenerator+      <*> opSeq sym l+      <*> opSeq sym r+    OMux b l r -> do+      b' <- opSeq sym b+      let b'' = if b' then WI.truePred sym else WI.falsePred sym+      S.SymSequenceMerge+        <$> Nonce.freshNonce Nonce.globalNonceGenerator+        <*> pure b''+        <*> opSeq sym l+        <*> opSeq sym r++    -- Operations+    OUncons l -> do+      l' <- opSeq sym l+      let interpPred p x y =+            if asConstPred (Just sym) p+            then pure x+            else pure y+      pe <- S.unconsSymSequence sym interpPred l'+      case pe of+        WP.Unassigned -> pure (Nothing, l')+        WP.PE _ (hd, tl) -> -- TODO: assert pred is truePred+          pure (Just hd, tl)+    OLength s -> do+      l <- S.lengthSymSequence sym =<< opSeq sym s+      case WI.asInteger (WI.natToIntegerPure l) of+        Just l' -> pure l'+        Nothing -> error "SymSequence: symbolic length"