-
Notifications
You must be signed in to change notification settings - Fork 63
/
Copy pathOverride.hs
406 lines (347 loc) · 13.6 KB
/
Override.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
{- |
Module : SAWScript.Crucible.Common.Override
Description : Language-neutral overrides
License : BSD3
Maintainer : langston
Stability : provisional
-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module SAWScript.Crucible.Common.Override
( Pointer
, MS.Pointer'
, OverrideState
, OverrideState'(..)
, osAsserts
, osAssumes
, osFree
, osLocation
, overrideGlobals
, syminterface
, setupValueSub
, termSub
, termEqs
--
, OverrideFailureReason(..)
, ppOverrideFailureReason
, OverrideFailure(..)
, ppOverrideFailure
--
, OverrideMatcher
, OverrideMatcher'(..)
, throwOverrideMatcher
, runOverrideMatcher
, RO
, RW
, addTermEq
, addAssert
, addAssume
, readGlobal
, writeGlobal
, failure
, getSymInterface
, enforceCompleteSubstitution
--
, assignmentToList
, MetadataMap
) where
import qualified Control.Exception as X
import Control.Lens
import Control.Monad (unless)
import Control.Monad.Trans.State hiding (get, put)
import Control.Monad.State.Class (MonadState(..))
import Control.Monad.Error.Class (MonadError)
import Control.Monad.Catch (MonadThrow)
import qualified Control.Monad.Fail as Fail
import Control.Monad.Trans.Except
import Control.Monad.Trans.Class
import Control.Monad.IO.Class
import qualified Data.Map as Map
import Data.Map (Map)
import Data.Set (Set)
import Data.Typeable (Typeable)
import Data.Void
import GHC.Generics (Generic, Generic1)
import qualified Prettyprinter as PP
import qualified Data.Parameterized.Context as Ctx
import Data.Parameterized.Some (Some)
import Data.Parameterized.TraversableFC (toListFC)
import Verifier.SAW.SharedTerm as SAWVerifier
import Verifier.SAW.TypedTerm as SAWVerifier
import qualified Lang.Crucible.CFG.Core as Crucible (TypeRepr, GlobalVar)
import qualified Lang.Crucible.Simulator.GlobalState as Crucible
import qualified Lang.Crucible.Simulator.RegMap as Crucible
import qualified Lang.Crucible.Simulator.SimError as Crucible
import qualified What4.Interface as W4
import qualified What4.LabeledPred as W4
import qualified What4.ProgramLoc as W4
import SAWScript.Exceptions
import SAWScript.Crucible.Common (Sym)
import SAWScript.Crucible.Common.MethodSpec as MS
import SAWScript.Crucible.Common.Setup.Value as MS
-- TODO, not sure this is the best place for this definition
type MetadataMap =
Map (W4.SymAnnotation Sym W4.BaseBoolType) MS.ConditionMetadata
--------------------------------------------------------------------------------
-- ** OverrideState
type LabeledPred sym = W4.LabeledPred (W4.Pred sym) Crucible.SimError
type Pointer ext = MS.Pointer' ext Sym
data OverrideState' sym ext = OverrideState
{ -- | Substitution for memory allocations
_setupValueSub :: Map AllocIndex (MS.Pointer' ext sym)
-- | Substitution for SAW Core external constants
, _termSub :: Map VarIndex Term
-- | Equalities of SAW Core terms
, _termEqs :: [(Term, ConditionMetadata, Crucible.SimError)]
-- | Free variables available for unification
, _osFree :: Set VarIndex
-- | Accumulated assertions
, _osAsserts :: [(ConditionMetadata, LabeledPred sym)]
-- | Accumulated assumptions
, _osAssumes :: [(ConditionMetadata, W4.Pred sym)]
-- | Symbolic simulation state
, _syminterface :: sym
-- | Global variables
, _overrideGlobals :: Crucible.SymGlobalState sym
-- | Source location to associated with this override
, _osLocation :: W4.ProgramLoc
}
type OverrideState = OverrideState' Sym
makeLenses ''OverrideState'
-- | The initial override matching state starts with an empty substitution
-- and no assertions or assumptions.
initialState ::
sym {- ^ simulator -} ->
Crucible.SymGlobalState sym {- ^ initial global variables -} ->
Map AllocIndex (Pointer' ext sym) {- ^ initial allocation substituion -} ->
Map VarIndex Term {- ^ initial term substituion -} ->
Set VarIndex {- ^ initial free terms -} ->
W4.ProgramLoc {- ^ location information for the override -} ->
OverrideState' sym ext
initialState sym globals allocs terms free loc = OverrideState
{ _osAsserts = []
, _osAssumes = []
, _syminterface = sym
, _overrideGlobals = globals
, _termSub = terms
, _termEqs = []
, _osFree = free
, _setupValueSub = allocs
, _osLocation = loc
}
--------------------------------------------------------------------------------
-- ** OverrideFailureReason
data OverrideFailureReason ext
= AmbiguousPointsTos [MS.PointsTo ext]
| AmbiguousVars [TypedExtCns]
| BadTermMatch Term Term -- ^ simulated and specified terms did not match
| BadPointerCast -- ^ Pointer required to process points-to
| BadReturnSpecification (Some Crucible.TypeRepr)
-- ^ type mismatch in return specification
| NonlinearPatternNotSupported
| BadEqualityComparison -- ^ Comparison on an undef value
| BadPointerLoad (Either (MS.PointsTo ext) (PP.Doc Void)) (PP.Doc Void)
-- ^ @loadRaw@ failed due to type error
| StructuralMismatch (PP.Doc Void) (PP.Doc Void) (Maybe (ExtType ext)) (ExtType ext)
-- ^
-- * pretty-printed simulated value
-- * pretty-printed specified value
-- * type of specified value
-- * type of simulated value
instance ( PP.Pretty (ExtType ext)
, PP.Pretty (MS.PointsTo ext)
) => PP.Pretty (OverrideFailureReason ext) where
pretty = ppOverrideFailureReason
instance ( PP.Pretty (ExtType ext)
, PP.Pretty (MS.PointsTo ext)
) => Show (OverrideFailureReason ext) where
show = show . PP.pretty
ppOverrideFailureReason ::
( PP.Pretty (ExtType ext)
, PP.Pretty (MS.PointsTo ext)
) => OverrideFailureReason ext -> PP.Doc ann
ppOverrideFailureReason rsn = case rsn of
AmbiguousPointsTos pts ->
PP.vcat
[ PP.pretty "LHS of points-to assertion(s) not reachable via points-tos from inputs/outputs:"
, PP.indent 2 $ PP.vcat (map PP.pretty pts)
]
AmbiguousVars vs ->
PP.vcat
[ PP.pretty "Fresh variable(s) not reachable via points-tos from function inputs/outputs:"
, PP.indent 2 $ PP.vcat (map MS.ppTypedExtCns vs)
]
BadTermMatch x y ->
PP.vcat
[ PP.pretty "terms do not match"
, PP.indent 2 (PP.unAnnotate (ppTerm defaultPPOpts x))
, PP.indent 2 (PP.unAnnotate (ppTerm defaultPPOpts y))
]
BadPointerCast ->
PP.pretty "bad pointer cast"
BadReturnSpecification ty ->
PP.vcat
[ PP.pretty "Spec had no return value, but the function returns a value of type:"
, PP.viaShow ty
]
NonlinearPatternNotSupported ->
PP.pretty "nonlinear pattern not supported"
BadEqualityComparison ->
PP.pretty "value containing `undef` compared for equality"
BadPointerLoad pointsTo msg ->
PP.vcat
[ PP.pretty "error when loading through pointer that" PP.<+>
PP.pretty "appeared in the override's points-to precondition(s):"
, PP.pretty "Precondition:"
, PP.indent 2 (either PP.pretty PP.unAnnotate pointsTo)
, PP.pretty "Failure reason: "
, PP.indent 2 (PP.unAnnotate msg) -- this can be long
]
StructuralMismatch simVal setupVal setupValTy ty ->
PP.vcat
[ PP.pretty "could not match specified value with actual value:"
, PP.vcat (map (PP.indent 2) $
[ PP.pretty "actual (simulator) value:" PP.<+> PP.unAnnotate simVal
, PP.pretty "specified value: " PP.<+> PP.unAnnotate setupVal
, PP.pretty "type of actual value: " PP.<+> PP.pretty ty
] ++ let msg ty_ =
[PP.pretty "type of specified value:"
PP.<+> PP.pretty ty_]
in maybe [] msg setupValTy)
]
--------------------------------------------------------------------------------
-- ** OverrideFailure
data OverrideFailure ext = OF W4.ProgramLoc (OverrideFailureReason ext)
ppOverrideFailure :: ( PP.Pretty (ExtType ext)
, PP.Pretty (MS.PointsTo ext)
) => OverrideFailure ext -> PP.Doc ann
ppOverrideFailure (OF loc rsn) =
PP.vcat
[ PP.pretty "at" PP.<+> PP.viaShow (W4.plSourceLoc loc) -- TODO: fix when what4 switches to prettyprinter
, ppOverrideFailureReason rsn
]
instance ( PP.Pretty (ExtType ext)
, PP.Pretty (MS.PointsTo ext)
) => PP.Pretty (OverrideFailure ext) where
pretty = ppOverrideFailure
instance ( PP.Pretty (ExtType ext)
, PP.Pretty (MS.PointsTo ext)
) => Show (OverrideFailure ext) where
show = show . PP.pretty
instance ( PP.Pretty (ExtType ext)
, PP.Pretty (MS.PointsTo ext)
, Typeable ext
) => X.Exception (OverrideFailure ext)
--------------------------------------------------------------------------------
-- ** OverrideMatcher
data RO
data RW
-- | The 'OverrideMatcher' type provides the operations that are needed
-- to match a specification's arguments with the arguments provided by
-- the Crucible simulation in order to compute the variable substitution
-- and side-conditions needed to proceed.
newtype OverrideMatcher' sym ext rorw m a =
OM (StateT (OverrideState' sym ext) (ExceptT (OverrideFailure ext) m) a)
deriving (Applicative, Functor, Generic, Generic1, Monad, MonadIO, MonadThrow)
type OverrideMatcher ext rorw a = OverrideMatcher' Sym ext rorw IO a
instance Wrapped (OverrideMatcher' sym ext rorw m a) where
deriving instance Monad m => MonadState (OverrideState' sym ext) (OverrideMatcher' sym ext rorw m)
deriving instance Monad m => MonadError (OverrideFailure ext) (OverrideMatcher' sym ext rorw m)
instance MonadTrans (OverrideMatcher' sym ext rorw) where
lift f = OM $ lift $ lift f
throwOverrideMatcher :: Monad m => String -> OverrideMatcher' sym ext rorw m a
throwOverrideMatcher msg = do
loc <- use osLocation
X.throw $ OverrideMatcherException loc msg
instance Monad m => Fail.MonadFail (OverrideMatcher' sym ext rorw m) where
fail = throwOverrideMatcher
-- | "Run" function for OverrideMatcher. The final result and state
-- are returned. The state will contain the updated globals and substitutions
runOverrideMatcher ::
Monad m =>
sym {- ^ simulator -} ->
Crucible.SymGlobalState sym {- ^ initial global variables -} ->
Map AllocIndex (Pointer' ext sym) {- ^ initial allocation substitution -} ->
Map VarIndex Term {- ^ initial term substitution -} ->
Set VarIndex {- ^ initial free variables -} ->
W4.ProgramLoc {- ^ override location information -} ->
OverrideMatcher' sym ext md m a {- ^ matching action -} ->
m (Either (OverrideFailure ext) (a, OverrideState' sym ext))
runOverrideMatcher sym g a t free loc (OM m) =
runExceptT (runStateT m (initialState sym g a t free loc))
addTermEq ::
Term {- ^ term equality -} ->
ConditionMetadata ->
Crucible.SimError {- ^ reason -} ->
OverrideMatcher ext rorw ()
addTermEq t md r =
OM (termEqs %= cons (t, md, r))
addAssert ::
Monad m =>
W4.Pred sym {- ^ property -} ->
ConditionMetadata ->
Crucible.SimError {- ^ reason -} ->
OverrideMatcher' sym ext rorw m ()
addAssert p md r =
OM (osAsserts %= cons (md,W4.LabeledPred p r))
addAssume ::
Monad m =>
W4.Pred sym {- ^ property -} ->
ConditionMetadata ->
OverrideMatcher' sym ext rorw m ()
addAssume p md = OM (osAssumes %= cons (md,p))
readGlobal ::
Monad m =>
Crucible.GlobalVar tp ->
OverrideMatcher' sym ext rorw m (Crucible.RegValue sym tp)
readGlobal k =
do mb <- OM (uses overrideGlobals (Crucible.lookupGlobal k))
case mb of
Nothing -> throwOverrideMatcher ("No such global: " ++ show k)
Just v -> return v
writeGlobal ::
Monad m =>
Crucible.GlobalVar tp ->
Crucible.RegValue sym tp ->
OverrideMatcher' sym ext RW m ()
writeGlobal k v = OM (overrideGlobals %= Crucible.insertGlobal k v)
-- | Abort the current computation by raising the given 'OverrideFailure'
-- exception.
failure ::
Monad m =>
W4.ProgramLoc ->
OverrideFailureReason ext ->
OverrideMatcher' sym ext md m a
failure loc e = OM (lift (throwE (OF loc e)))
getSymInterface :: Monad m => OverrideMatcher' sym ext md m sym
getSymInterface = OM (use syminterface)
-- | Verify that all of the fresh variables for the given
-- state spec have been "learned". If not, throws
-- 'AmbiguousVars' exception.
enforceCompleteSubstitution ::
W4.ProgramLoc ->
MS.StateSpec ext ->
OverrideMatcher ext w ()
enforceCompleteSubstitution loc ss =
do sub <- OM (use termSub)
let -- predicate matches terms that are not covered by the computed
-- term substitution
isMissing tt = ecVarIndex (tecExt tt) `Map.notMember` sub
-- list of all terms not covered by substitution
missing = filter isMissing (view MS.csFreshVars ss)
unless (null missing) (failure loc (AmbiguousVars missing))
------------------------------------------------------------------------
-- | Forget the type indexes and length of the arguments.
assignmentToList ::
Ctx.Assignment (Crucible.RegEntry sym) ctx ->
[Crucible.AnyValue sym]
assignmentToList = toListFC (\(Crucible.RegEntry x y) -> Crucible.AnyValue x y)