-
Notifications
You must be signed in to change notification settings - Fork 62
/
Builtins.hs
1661 lines (1484 loc) · 64.4 KB
/
Builtins.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
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{- |
Module : SAWScript.Crucible.LLVM.Builtins
Description : Implementations of Crucible-related SAW-Script primitives.
License : BSD3
Maintainer : atomb
Stability : provisional
-}
{-# LANGUAGE FlexibleContexts, FlexibleInstances, TypeSynonymInstances, MultiParamTypeClasses#-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ParallelListComp #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module SAWScript.Crucible.LLVM.Builtins
( show_cfg
, crucible_execute_func
, crucible_return
, crucible_precond
, crucible_postcond
, crucible_llvm_cfg
, crucible_llvm_extract
, crucible_llvm_verify
, crucible_llvm_array_size_profile
, crucible_setup_val_to_typed_term
, crucible_spec_size
, crucible_spec_solvers
, crucible_ghost_value
, crucible_declare_ghost_state
, crucible_equal
, crucible_points_to
, crucible_fresh_pointer
, crucible_llvm_unsafe_assume_spec
, crucible_fresh_var
, crucible_alloc
, crucible_alloc_readonly
, crucible_alloc_with_size
, crucible_alloc_global
, crucible_fresh_expanded_val
--
-- These function are common to LLVM & JVM implementation (not for external use)
, setupArg
, setupArgs
, getGlobalPair
, runCFG
) where
import Prelude hiding (fail)
import Control.Lens
import Control.Monad.State hiding (fail)
import Control.Monad.Fail (MonadFail(..))
import qualified Data.Bimap as Bimap
import Data.Char (isDigit)
import Data.Foldable (for_, toList, find, fold)
import Data.Function
import Data.IORef
import Data.List
import Data.List.Extra (nubOrd)
import qualified Data.List.NonEmpty as NE
import Data.Maybe
import Data.String
import Data.Map (Map)
import qualified Data.Map as Map
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import qualified Data.Set as Set
import Data.Sequence (Seq)
import qualified Data.Sequence as Seq
import qualified Data.Text as Text
import qualified Data.Vector as V
import Numeric.Natural
import System.IO
import qualified Text.LLVM.AST as L
import qualified Text.LLVM.PP as L (ppType, ppSymbol)
import Text.PrettyPrint.ANSI.Leijen hiding ((<$>), (<>))
import qualified Text.PrettyPrint.ANSI.Leijen as PP
import qualified Control.Monad.Trans.Maybe as MaybeT
-- parameterized-utils
import Data.Parameterized.Classes
import Data.Parameterized.NatRepr
import Data.Parameterized.Nonce
import Data.Parameterized.Some
-- cryptol
import qualified Cryptol.TypeCheck.Type as Cryptol
-- what4
import qualified What4.Concrete as W4
import qualified What4.Config as W4
import qualified What4.FunctionName as W4
import qualified What4.LabeledPred as W4
import qualified What4.ProgramLoc as W4
import qualified What4.Interface as W4
import qualified What4.Expr.Builder as W4
-- crucible
import qualified Lang.Crucible.Backend as Crucible
import qualified Lang.Crucible.Backend.SAWCore as CrucibleSAW
import qualified Lang.Crucible.CFG.Core as Crucible
import qualified Lang.Crucible.CFG.Extension as Crucible
(IsSyntaxExtension)
import qualified Lang.Crucible.FunctionHandle as Crucible
import qualified Lang.Crucible.Simulator as Crucible
import qualified Lang.Crucible.Simulator.Breakpoint as Crucible
import qualified Lang.Crucible.Simulator.GlobalState as Crucible
import qualified Lang.Crucible.Simulator.PathSatisfiability as Crucible
import qualified Lang.Crucible.Simulator.SimError as Crucible
-- crucible-llvm
import qualified Lang.Crucible.LLVM.ArraySizeProfile as Crucible
import qualified Lang.Crucible.LLVM.DataLayout as Crucible
import Lang.Crucible.LLVM.Extension (LLVM)
import qualified Lang.Crucible.LLVM.MemModel as Crucible
import qualified Lang.Crucible.LLVM.Translation as Crucible
import qualified SAWScript.Crucible.LLVM.CrucibleLLVM as Crucible
-- parameterized-utils
import qualified Data.Parameterized.TraversableFC as Ctx
import qualified Data.Parameterized.Context as Ctx
-- saw-core
import Verifier.SAW.FiniteValue (ppFirstOrderValue)
import Verifier.SAW.SharedTerm
import Verifier.SAW.TypedAST
import Verifier.SAW.Recognizer
import Verifier.SAW.TypedTerm
-- saw-script
import SAWScript.Proof
import SAWScript.Prover.SolverStats
import SAWScript.Prover.Versions
import SAWScript.TopLevel
import SAWScript.Value
import SAWScript.Position as SS
import SAWScript.Options
import qualified SAWScript.Crucible.Common as Common
import SAWScript.Crucible.Common (Sym)
import SAWScript.Crucible.Common.MethodSpec (AllocIndex(..), nextAllocIndex, PrePost(..))
import qualified SAWScript.Crucible.Common.MethodSpec as MS
import SAWScript.Crucible.Common.MethodSpec (SetupValue(..))
import qualified SAWScript.Crucible.Common.Setup.Builtins as Setup
import qualified SAWScript.Crucible.Common.Setup.Type as Setup
import SAWScript.Crucible.LLVM.Override
import SAWScript.Crucible.LLVM.ResolveSetupValue
import SAWScript.Crucible.LLVM.MethodSpecIR
type MemImpl = Crucible.MemImpl Sym
data LLVMVerificationException
= MultipleStaticFunctions L.Symbol
| DefNotFound L.Symbol [L.Symbol]
| DeclNotFound L.Symbol [L.Symbol]
| SetupError SetupError
displayVerifExceptionOpts :: Options -> LLVMVerificationException -> String
displayVerifExceptionOpts _ (MultipleStaticFunctions (L.Symbol nm)) =
"Multiple non-equal definitions for `" ++ nm ++ "`."
displayVerifExceptionOpts opts (DefNotFound (L.Symbol nm) nms) =
unlines $
[ "Could not find definition for function named `" ++ nm ++ "`."
] ++ if simVerbose opts < 3
then [ "Run SAW with --sim-verbose=3 to see all function names" ]
else "Available function names:" : map ((" " ++) . show . L.ppSymbol) nms
displayVerifExceptionOpts opts (DeclNotFound (L.Symbol nm) nms) =
unlines $
[ "Could not find declaration for function named `" ++ nm ++ "`."
] ++ if simVerbose opts < 3
then [ "Run SAW with --sim-verbose=3 to see all function names" ]
else "Available function names:" : map ((" " ++) . show . L.ppSymbol) nms
displayVerifExceptionOpts _ (SetupError e) =
"Error during simulation setup: " ++ show (ppSetupError e)
show_cfg :: SAW_CFG -> String
show_cfg (LLVM_CFG (Crucible.AnyCFG cfg)) = show cfg
show_cfg (JVM_CFG (Crucible.AnyCFG cfg)) = show cfg
-- | Determines whether one LLVM symbol is equivalent to another except
-- for a numeric suffix. This can determine whether one symbol is the
-- disambiguated name of a duplicated static function.
matchingStatics :: L.Symbol -> L.Symbol -> Bool
matchingStatics (L.Symbol a) (L.Symbol b) = go a b
where
go [] [] = True
go (x:xs) (y:ys) = x == y && go xs ys
go [] ('.':ds) = all isDigit ds
go ('.':ds) [] = all isDigit ds
go _ _ = False
findDefMaybeStatic :: L.Module -> String -> Either LLVMVerificationException (NE.NonEmpty L.Define)
findDefMaybeStatic llmod nm = do
case NE.nonEmpty (filter (\d -> matchingStatics (L.defName d) nm') (L.modDefines llmod)) of
Nothing -> Left $ DefNotFound nm' $ map L.defName $ L.modDefines llmod
Just defs -> Right defs
where
nm' = fromString nm
findDecl :: L.Module -> String -> Either LLVMVerificationException L.Declare
findDecl llmod nm = do
case find (\d -> (L.decName d) == nm') (L.modDeclares llmod) of
Just decl -> Right decl
Nothing -> Left $ DeclNotFound nm' $ map L.decName $ L.modDeclares llmod
where
nm' = fromString nm
resolveSpecName :: String -> TopLevel (String, Maybe String)
resolveSpecName nm = if Crucible.testBreakpointFunction nm
then return
( (takeWhile (not . (== '#')) nm)
, Just (tail (dropWhile (not . (== '#')) nm))
)
else return (nm, Nothing)
crucible_llvm_verify ::
BuiltinContext ->
Options ->
Some LLVMModule ->
String ->
[SomeLLVM MS.CrucibleMethodSpecIR] ->
Bool ->
LLVMCrucibleSetupM () ->
ProofScript SatResult ->
TopLevel (SomeLLVM MS.CrucibleMethodSpecIR)
crucible_llvm_verify bic opts (Some lm) nm lemmas checkSat setup tactic = do
lemmas' <- checkModuleCompatibility lm lemmas
SomeLLVM <$>
createMethodSpec (Just (lemmas', checkSat, tactic)) Nothing bic opts lm nm setup
crucible_llvm_unsafe_assume_spec ::
BuiltinContext ->
Options ->
Some LLVMModule ->
String {- ^ Name of the function -} ->
LLVMCrucibleSetupM () {- ^ Boundary specification -} ->
TopLevel (SomeLLVM MS.CrucibleMethodSpecIR)
crucible_llvm_unsafe_assume_spec bic opts (Some lm) nm setup =
SomeLLVM <$> createMethodSpec Nothing Nothing bic opts lm nm setup
crucible_llvm_array_size_profile ::
BuiltinContext ->
Options ->
Some LLVMModule ->
String ->
LLVMCrucibleSetupM () ->
TopLevel [Crucible.Profile]
crucible_llvm_array_size_profile bic opts (Some lm) nm setup = do
cell <- io $ newIORef Map.empty
void $ createMethodSpec (Just ([], False, undefined)) (Just cell) bic opts lm nm setup
profiles <- io $ readIORef cell
pure $ Map.toList profiles
-- | Check that all the overrides/lemmas were actually from this module
checkModuleCompatibility ::
MonadFail m =>
LLVMModule arch ->
[SomeLLVM MS.CrucibleMethodSpecIR] ->
m [MS.CrucibleMethodSpecIR (LLVM arch)]
checkModuleCompatibility llvmModule = foldM step []
where step accum (SomeLLVM lemma) =
case testEquality (lemma ^. MS.csCodebase) llvmModule of
Nothing -> fail $ unlines
[ "Failed to apply an override that was verified against a"
, "different LLVM module"
]
Just Refl -> pure (lemma:accum)
-- | The real work of 'crucible_llvm_verify' and 'crucible_llvm_unsafe_assume_spec'.
createMethodSpec ::
Maybe ([MS.CrucibleMethodSpecIR (LLVM arch)], Bool, ProofScript SatResult)
{- ^ If verifying, provide lemmas, branch sat checking, tactic -} ->
Maybe (IORef (Map Text.Text [[Maybe Int]])) ->
BuiltinContext ->
Options ->
LLVMModule arch ->
String {- ^ Name of the function -} ->
LLVMCrucibleSetupM () {- ^ Boundary specification -} ->
TopLevel (MS.CrucibleMethodSpecIR (LLVM arch))
createMethodSpec verificationArgs asp bic opts lm@(LLVMModule _ _ mtrans) nm setup = do
(nm', parent) <- resolveSpecName nm
let edef = findDefMaybeStatic (lm ^. modAST) nm'
let edecl = findDecl (lm ^. modAST) nm'
defOrDecls <- case (edef, edecl) of
(Right defs, _) -> return (NE.map Left defs)
(_, Right decl) -> return (Right decl NE.:| [])
(Left err, Left _) -> fail (displayVerifExceptionOpts opts err)
let ?lc = mtrans ^. Crucible.transContext . Crucible.llvmTypeCtx
profFile <- rwProfilingFile <$> getTopLevelRW
Crucible.llvmPtrWidth (mtrans ^. Crucible.transContext) $ \_ ->
fmap NE.head $ forM defOrDecls $ \defOrDecl -> do
setupLLVMCrucibleContext bic opts lm $ \cc -> do
let sym = cc^.ccBackend
(writeFinalProfile, pfs) <- io $ Common.setupProfiling sym "crucible_llvm_verify" profFile
pos <- getPosition
let setupLoc = toW4Loc "_SAW_verify_prestate" pos
let est0 =
case defOrDecl of
Left def -> initialCrucibleSetupState cc def setupLoc parent
Right decl -> initialCrucibleSetupStateDecl cc decl setupLoc parent
st0 <- either (fail . show . ppSetupError) return est0
-- execute commands of the method spec
liftIO $ W4.setCurrentProgramLoc sym setupLoc
methodSpec <- view Setup.csMethodSpec <$>
execStateT (runLLVMCrucibleSetupM setup) st0
void $ io $ checkSpecReturnType cc methodSpec
case verificationArgs of
-- If we're just admitting, don't do anything
Nothing -> do
printOutLnTop Info $
unwords ["Assume override", (methodSpec ^. csName) ]
return methodSpec
-- If we're verifying, actually perform the verification
Just (lemmas, checkSat, tactic) -> do
printOutLnTop Info $
unwords ["Verifying", (methodSpec ^. csName) , "..."]
-- set up the LLVM memory with a pristine heap
let globals = cc^.ccLLVMGlobals
let mvar = Crucible.llvmMemVar (cc^.ccLLVMContext)
mem0 <- case Crucible.lookupGlobal mvar globals of
Nothing -> fail "internal error: LLVM Memory global not found"
Just mem0 -> return mem0
-- push a memory stack frame if starting from a breakpoint
let mem = if isJust (methodSpec^.csParentName)
then mem0
{ Crucible.memImplHeap = Crucible.pushStackFrameMem
(Crucible.memImplHeap mem0)
}
else mem0
let globals1 = Crucible.llvmGlobals (cc^.ccLLVMContext) mem
-- construct the initial state for verifications
(args, assumes, env, globals2) <-
io $ verifyPrestate opts cc methodSpec globals1
-- save initial path conditions
frameIdent <- io $ Crucible.pushAssumptionFrame sym
-- run the symbolic execution
printOutLnTop Info $
unwords ["Simulating", (methodSpec ^. csName) , "..."]
top_loc <- toW4Loc "crucible_llvm_verify" <$> getPosition
(ret, globals3)
<- io $ verifySimulate opts cc pfs methodSpec args assumes top_loc lemmas globals2 checkSat asp
-- collect the proof obligations
asserts <- verifyPoststate opts (biSharedContext bic) cc
methodSpec env globals3 ret
-- restore previous assumption state
_ <- io $ Crucible.popAssumptionFrame sym frameIdent
-- attempt to verify the proof obligations
printOutLnTop Info $
unwords ["Checking proof obligations", (methodSpec ^. csName), "..."]
stats <- verifyObligations cc methodSpec tactic assumes asserts
io $ writeFinalProfile
return (methodSpec & MS.csSolverStats .~ stats)
verifyObligations :: LLVMCrucibleContext arch
-> MS.CrucibleMethodSpecIR (LLVM arch)
-> ProofScript SatResult
-> [Crucible.LabeledPred Term Crucible.AssumptionReason]
-> [(String, Term)]
-> TopLevel SolverStats
verifyObligations cc mspec tactic assumes asserts = do
let sym = cc^.ccBackend
st <- io $ readIORef $ W4.sbStateManager sym
let sc = CrucibleSAW.saw_ctx st
assume <- io $ scAndList sc (toListOf (folded . Crucible.labeledPred) assumes)
let nm = mspec ^. csName
stats <- forM (zip [(0::Int)..] asserts) $ \(n, (msg, assert)) -> do
goal <- io $ scImplies sc assume assert
goal' <- io $ scGeneralizeExts sc (getAllExts goal) =<< scEqTrue sc goal
let goalname = concat [nm, " (", takeWhile (/= '\n') msg, ")"]
proofgoal = ProofGoal n "vc" goalname (Prop goal')
r <- evalStateT tactic (startProof proofgoal)
case r of
Unsat stats -> return stats
SatMulti stats vals -> do
printOutLnTop Info $ unwords ["Subgoal failed:", nm, msg]
printOutLnTop Info (show stats)
printOutLnTop OnlyCounterExamples "----------Counterexample----------"
opts <- sawPPOpts <$> rwPPOpts <$> getTopLevelRW
if null vals then
printOutLnTop OnlyCounterExamples "<<All settings of the symbolic variables constitute a counterexample>>"
else
let showAssignment (name, val) = " " ++ name ++ ": " ++ show (ppFirstOrderValue opts val) in
mapM_ (printOutLnTop OnlyCounterExamples . showAssignment) vals
printOutLnTop OnlyCounterExamples "----------------------------------"
fail "Proof failed." -- Mirroring behavior of llvm_verify
printOutLnTop Info $ unwords ["Proof succeeded!", nm]
return (mconcat stats)
-- | Check that the specified return value has the expected type
--
-- The expected type is inferred from the LLVM module.
--
-- TODO: generalize, put in Setup.Builtins
checkSpecReturnType ::
Crucible.HasPtrWidth (Crucible.ArchWidth arch) =>
LLVMCrucibleContext arch ->
MS.CrucibleMethodSpecIR (LLVM arch) ->
IO ()
checkSpecReturnType cc mspec =
case (mspec ^. MS.csRetValue, mspec ^. MS.csRet) of
(Just _, Nothing) ->
fail $ unlines
[ "Could not resolve return type of " ++ mspec ^. csName
, "Raw type: " ++ show (mspec ^. MS.csRet)
]
(Just sv, Just retTy) -> do
retTy' <-
typeOfSetupValue cc
(MS.csAllocations mspec) -- map allocation indices to allocations
(mspec ^. MS.csPreState . MS.csVarTypeNames) -- map alloc indices to var names
sv
-- This check is too lax, see saw-script#443
b <- checkRegisterCompatibility retTy retTy'
unless b $ fail $ unlines
[ "Incompatible types for return value when verifying " ++ mspec^.csName
, "Expected: " ++ show retTy
, "but given value of type: " ++ show retTy'
]
(Nothing, _) -> return ()
-- | Evaluate the precondition part of a Crucible method spec:
--
-- * Allocate heap space for each 'crucible_alloc' statement.
--
-- * Record an equality precondition for each 'crucible_equal'
-- statement.
--
-- * Write to memory for each 'crucible_points_to' statement. (Writes
-- to already-initialized locations are transformed into equality
-- preconditions.)
--
-- * Evaluate the function arguments from the 'crucible_execute_func'
-- statement.
--
-- Returns a tuple of (arguments, preconditions, pointer values,
-- memory).
verifyPrestate ::
Crucible.HasPtrWidth (Crucible.ArchWidth arch) =>
Options ->
LLVMCrucibleContext arch ->
MS.CrucibleMethodSpecIR (LLVM arch) ->
Crucible.SymGlobalState Sym ->
IO ([(Crucible.MemType, LLVMVal)],
[Crucible.LabeledPred Term Crucible.AssumptionReason],
Map AllocIndex (LLVMPtr (Crucible.ArchWidth arch)),
Crucible.SymGlobalState Sym)
verifyPrestate opts cc mspec globals = do
let ?lc = cc^.ccTypeCtx
let sym = cc^.ccBackend
let prestateLoc = W4.mkProgramLoc "_SAW_verify_prestate" W4.InternalPos
liftIO $ W4.setCurrentProgramLoc sym prestateLoc
let lvar = Crucible.llvmMemVar (cc^.ccLLVMContext)
let Just mem = Crucible.lookupGlobal lvar globals
-- Allocate LLVM memory for each 'crucible_alloc'
(env1, mem') <- runStateT
(traverse (doAlloc cc) $ mspec ^. MS.csPreState . MS.csAllocs)
mem
env2 <- Map.traverseWithKey
(\k _ -> executeFreshPointer cc k)
(mspec ^. MS.csPreState . MS.csFreshPointers)
let env = Map.unions [env1, env2]
mem'' <- setupGlobalAllocs cc (mspec ^. MS.csGlobalAllocs) mem'
mem''' <- setupPrePointsTos mspec opts cc env (mspec ^. MS.csPreState . MS.csPointsTos) mem''
let globals1 = Crucible.insertGlobal lvar mem''' globals
(globals2,cs) <- setupPrestateConditions mspec cc mem''' env globals1 (mspec ^. MS.csPreState . MS.csConditions)
args <- resolveArguments cc mem''' mspec env
return (args, cs, env, globals2)
-- | Check two MemTypes for register compatiblity. This is a stricter
-- check than the memory compatiblity check that is done for points-to
-- assertions.
checkRegisterCompatibility ::
(Crucible.HasPtrWidth wptr) =>
Crucible.MemType ->
Crucible.MemType ->
IO Bool
checkRegisterCompatibility mt mt' =
do st <- Crucible.toStorableType mt
st' <- Crucible.toStorableType mt'
return (st == st')
resolveArguments ::
(?lc :: Crucible.TypeContext, Crucible.HasPtrWidth (Crucible.ArchWidth arch)) =>
LLVMCrucibleContext arch ->
Crucible.MemImpl Sym ->
MS.CrucibleMethodSpecIR (LLVM arch) ->
Map AllocIndex (LLVMPtr (Crucible.ArchWidth arch)) ->
IO [(Crucible.MemType, LLVMVal)]
resolveArguments cc mem mspec env = mapM resolveArg [0..(nArgs-1)]
where
nArgs = toInteger (length (mspec ^. MS.csArgs))
tyenv = MS.csAllocations mspec
nameEnv = mspec ^. MS.csPreState . MS.csVarTypeNames
nm = mspec^.csName
checkArgTy i mt mt' =
do b <- checkRegisterCompatibility mt mt'
unless b $
fail $ unlines [ "Type mismatch in argument " ++ show i ++ " when veriyfing " ++ show nm
, "Argument is declared with type: " ++ show mt
, "but provided argument has incompatible type: " ++ show mt'
, "Note: this may be because the signature of your " ++
"function changed during compilation. If using " ++
"Clang, check the signature in the disassembled " ++
".ll file."
]
resolveArg i =
case Map.lookup i (mspec ^. MS.csArgBindings) of
Just (mt, sv) -> do
mt' <- typeOfSetupValue cc tyenv nameEnv sv
checkArgTy i mt mt'
v <- resolveSetupVal cc mem env tyenv nameEnv sv
return (mt, v)
Nothing -> fail $ unwords ["Argument", show i, "unspecified when verifying", show nm]
--------------------------------------------------------------------------------
-- | For each "crucible_global_alloc" in the method specification, allocate and
-- register the appropriate memory.
setupGlobalAllocs :: forall arch.
(?lc :: Crucible.TypeContext, Crucible.HasPtrWidth (Crucible.ArchWidth arch)) =>
LLVMCrucibleContext arch ->
[MS.AllocGlobal (LLVM arch)] ->
MemImpl ->
IO MemImpl
setupGlobalAllocs cc allocs mem0 = foldM go mem0 allocs
where
sym = cc ^. ccBackend
go :: MemImpl -> MS.AllocGlobal (LLVM arch) -> IO MemImpl
go mem (LLVMAllocGlobal _ symbol@(L.Symbol name)) = do
let LLVMModule _ _ mtrans = cc ^. ccLLVMModule
gimap = Crucible.globalInitMap mtrans
case Map.lookup symbol gimap of
Just (g, Right (mt, _)) -> do
when (L.gaConstant $ L.globalAttrs g) . fail $ mconcat
[ "Global variable \""
, name
, "\" is not mutable"
]
let sz = Crucible.memTypeSize (Crucible.llvmDataLayout ?lc) mt
sz' <- W4.bvLit sym ?ptrWidth $ Crucible.bytesToInteger sz
alignment <-
case L.globalAlign g of
Just a | a > 0 ->
case Crucible.toAlignment $ Crucible.toBytes a of
Nothing -> fail $ mconcat
[ "Global variable \""
, name
, "\" has invalid alignment: "
, show a
]
Just al -> return al
_ -> pure $ Crucible.memTypeAlign (Crucible.llvmDataLayout ?lc) mt
(ptr, mem') <- Crucible.doMalloc sym Crucible.GlobalAlloc Crucible.Mutable name mem sz' alignment
pure $ Crucible.registerGlobal mem' [symbol] ptr
_ -> fail $ mconcat
[ "Global variable \""
, name
, "\" does not exist"
]
-- | For each points-to constraint in the pre-state section of the
-- function spec, write the given value to the address of the given
-- pointer.
setupPrePointsTos :: forall arch.
(?lc :: Crucible.TypeContext, Crucible.HasPtrWidth (Crucible.ArchWidth arch)) =>
MS.CrucibleMethodSpecIR (LLVM arch) ->
Options ->
LLVMCrucibleContext arch ->
Map AllocIndex (LLVMPtr (Crucible.ArchWidth arch)) ->
[MS.PointsTo (LLVM arch)] ->
MemImpl ->
IO MemImpl
setupPrePointsTos mspec opts cc env pts mem0 = foldM go mem0 pts
where
tyenv = MS.csAllocations mspec
nameEnv = mspec ^. MS.csPreState . MS.csVarTypeNames
go :: MemImpl -> MS.PointsTo (LLVM arch) -> IO MemImpl
go mem (LLVMPointsTo _loc ptr val) =
do ptr' <- resolveSetupVal cc mem env tyenv nameEnv ptr
ptr'' <- case ptr' of
Crucible.LLVMValInt blk off
| Just Refl <- testEquality (W4.bvWidth off) Crucible.PtrWidth
-> return (Crucible.LLVMPointer blk off)
_ -> fail "Non-pointer value found in points-to assertion"
storePointsToValue opts cc env tyenv nameEnv mem ptr'' val
-- | Sets up globals (ghost variable), and collects boolean terms
-- that should be assumed to be true.
setupPrestateConditions ::
(?lc :: Crucible.TypeContext, Crucible.HasPtrWidth (Crucible.ArchWidth arch)) =>
MS.CrucibleMethodSpecIR (LLVM arch) ->
LLVMCrucibleContext arch ->
Crucible.MemImpl Sym ->
Map AllocIndex (LLVMPtr (Crucible.ArchWidth arch)) ->
Crucible.SymGlobalState Sym ->
[MS.SetupCondition (LLVM arch)] ->
IO (Crucible.SymGlobalState Sym, [Crucible.LabeledPred Term Crucible.AssumptionReason])
setupPrestateConditions mspec cc mem env = aux []
where
tyenv = MS.csAllocations mspec
nameEnv = mspec ^. MS.csPreState . MS.csVarTypeNames
aux acc globals [] = return (globals, acc)
aux acc globals (MS.SetupCond_Equal loc val1 val2 : xs) =
do val1' <- resolveSetupVal cc mem env tyenv nameEnv val1
val2' <- resolveSetupVal cc mem env tyenv nameEnv val2
t <- assertEqualVals cc val1' val2'
let lp = Crucible.LabeledPred t (Crucible.AssumptionReason loc "equality precondition")
aux (lp:acc) globals xs
aux acc globals (MS.SetupCond_Pred loc tm : xs) =
let lp = Crucible.LabeledPred (ttTerm tm) (Crucible.AssumptionReason loc "precondition") in
aux (lp:acc) globals xs
aux acc globals (MS.SetupCond_Ghost () _loc var val : xs) =
aux acc (Crucible.insertGlobal var val globals) xs
--------------------------------------------------------------------------------
-- | Create a SAWCore formula asserting that two 'LLVMVal's are equal.
assertEqualVals ::
LLVMCrucibleContext arch ->
LLVMVal ->
LLVMVal ->
IO Term
assertEqualVals cc v1 v2 =
CrucibleSAW.toSC (cc^.ccBackend) =<< equalValsPred cc v1 v2
--------------------------------------------------------------------------------
-- TODO(langston): combine with/move to executeAllocation
doAlloc ::
(?lc :: Crucible.TypeContext, Crucible.HasPtrWidth (Crucible.ArchWidth arch)) =>
LLVMCrucibleContext arch ->
LLVMAllocSpec ->
StateT MemImpl IO (LLVMPtr (Crucible.ArchWidth arch))
doAlloc cc (LLVMAllocSpec mut _memTy sz loc) = StateT $ \mem ->
do let sym = cc^.ccBackend
let dl = Crucible.llvmDataLayout ?lc
sz' <- W4.bvLit sym Crucible.PtrWidth $ Crucible.bytesToInteger sz
let alignment = Crucible.maxAlignment dl -- Use the maximum alignment required for any primitive type (FIXME?)
let l = show (W4.plSourceLoc loc)
liftIO $
Crucible.doMalloc sym Crucible.HeapAlloc mut l mem sz' alignment
--------------------------------------------------------------------------------
ppAbortedResult :: LLVMCrucibleContext arch
-> Crucible.AbortedResult Sym a
-> Doc
ppAbortedResult cc = Common.ppAbortedResult (ppGlobalPair cc)
ppGlobalPair :: LLVMCrucibleContext arch
-> Crucible.GlobalPair Sym a
-> Doc
ppGlobalPair cc gp =
let mvar = Crucible.llvmMemVar (cc^.ccLLVMContext)
globals = gp ^. Crucible.gpGlobals in
case Crucible.lookupGlobal mvar globals of
Nothing -> text "LLVM Memory global variable not initialized"
Just mem -> Crucible.ppMem mem
--------------------------------------------------------------------------------
registerOverride ::
(?lc :: Crucible.TypeContext, Crucible.HasPtrWidth wptr, wptr ~ Crucible.ArchWidth arch) =>
Options ->
LLVMCrucibleContext arch ->
Crucible.SimContext (CrucibleSAW.SAWCruciblePersonality Sym) Sym (Crucible.LLVM arch) ->
W4.ProgramLoc ->
[MS.CrucibleMethodSpecIR (LLVM arch)] ->
Crucible.OverrideSim (CrucibleSAW.SAWCruciblePersonality Sym) Sym (Crucible.LLVM arch) rtp args ret ()
registerOverride opts cc _ctx top_loc cs = do
let sym = cc^.ccBackend
sc <- CrucibleSAW.saw_ctx <$> liftIO (readIORef (W4.sbStateManager sym))
let fstr = (head cs)^.csName
fsym = L.Symbol fstr
llvmctx = cc^.ccLLVMContext
matches (Crucible.LLVMHandleInfo _ h) =
matchingStatics (L.Symbol (Text.unpack (W4.functionName (Crucible.handleName h)))) fsym
liftIO $
printOutLn opts Info $ "Registering overrides for `" ++ fstr ++ "`"
case filter matches (Map.elems (llvmctx ^. Crucible.symbolMap)) of
[] -> fail $ "Couldn't find declaration for `" ++ fstr ++ "` when registering override for it."
-- LLVMHandleInfo constructor has two existential type arguments,
-- which are bound here. h :: FnHandle args' ret'
his -> forM_ his $ \(Crucible.LLVMHandleInfo _ h) -> do
-- TODO: check that decl' matches (csDefine cs)
let retType = Crucible.handleReturnType h
let hName = Crucible.handleName h
liftIO $
printOutLn opts Info $ " variant `" ++ show hName ++ "`"
Crucible.bindFnHandle h
$ Crucible.UseOverride
$ Crucible.mkOverride'
hName
retType
(methodSpecHandler opts sc cc top_loc cs h)
registerInvariantOverride
:: (?lc :: Crucible.TypeContext, Crucible.HasPtrWidth (Crucible.ArchWidth arch))
=> Options
-> LLVMCrucibleContext arch
-> W4.ProgramLoc
-> HashMap Crucible.SomeHandle [Crucible.BreakpointName]
-> [MS.CrucibleMethodSpecIR (LLVM arch)]
-> IO (Crucible.ExecutionFeature (CrucibleSAW.SAWCruciblePersonality Sym) Sym (Crucible.LLVM arch) rtp)
registerInvariantOverride opts cc top_loc all_breakpoints cs = do
sc <- CrucibleSAW.saw_ctx <$>
(liftIO $ readIORef $ W4.sbStateManager $ cc^.ccBackend)
let name = (head cs) ^. csName
parent <- case nubOrd $ map (view csParentName) cs of
[Just unique_parent] -> return unique_parent
_ -> fail $ "Multiple parent functions for breakpoint: " ++ name
liftIO $ printOutLn opts Info $ "Registering breakpoint `" ++ name ++ "`"
withBreakpointCfgAndBlockId cc name parent $ \cfg breakpoint_block_id -> do
let breakpoint_name = Crucible.BreakpointName $ Text.pack name
let h = Crucible.cfgHandle cfg
let arg_types = Crucible.blockInputs $
Crucible.getBlock breakpoint_block_id $
Crucible.cfgBlockMap cfg
let ret_type = Crucible.handleReturnType h
let halloc = Crucible.simHandleAllocator (cc ^. ccLLVMSimContext)
hInvariant <- Crucible.mkHandle' halloc (W4.plFunction top_loc) arg_types ret_type
Crucible.breakAndReturn
cfg
breakpoint_name
arg_types
ret_type
(methodSpecHandler opts sc cc top_loc cs hInvariant)
all_breakpoints
--------------------------------------------------------------------------------
withCfg
:: (?lc :: Crucible.TypeContext, Crucible.HasPtrWidth (Crucible.ArchWidth arch))
=> LLVMCrucibleContext arch
-> String
-> (forall blocks init ret . Crucible.CFG (Crucible.LLVM arch) blocks init ret -> IO a)
-> IO a
withCfg context name k = do
let function_id = L.Symbol name
case Map.lookup function_id (Crucible.cfgMap (context^.ccLLVMModuleTrans)) of
Just (Crucible.AnyCFG cfg) -> k cfg
Nothing -> fail $ "Unexpected function name: " ++ name
withCfgAndBlockId
:: (?lc :: Crucible.TypeContext, Crucible.HasPtrWidth (Crucible.ArchWidth arch))
=> LLVMCrucibleContext arch
-> MS.CrucibleMethodSpecIR (LLVM arch)
-> (forall blocks init args ret . Crucible.CFG (Crucible.LLVM arch) blocks init ret -> Crucible.BlockID blocks args -> IO a)
-> IO a
withCfgAndBlockId context method_spec k = case method_spec ^. csParentName of
Nothing -> withCfg context (method_spec ^. csName) $ \cfg ->
k cfg (Crucible.cfgEntryBlockID cfg)
Just parent -> withBreakpointCfgAndBlockId
context
(method_spec ^. csName)
parent
k
withBreakpointCfgAndBlockId
:: (?lc :: Crucible.TypeContext, Crucible.HasPtrWidth (Crucible.ArchWidth arch))
=> LLVMCrucibleContext arch
-> String
-> String
-> (forall blocks init args ret . Crucible.CFG (Crucible.LLVM arch) blocks init ret -> Crucible.BlockID blocks args -> IO a)
-> IO a
withBreakpointCfgAndBlockId context name parent k = do
let breakpoint_name = Crucible.BreakpointName $ Text.pack name
withCfg context parent $ \cfg ->
case Bimap.lookup breakpoint_name (Crucible.cfgBreakpoints cfg) of
Just (Some breakpoint_block_id) -> k cfg breakpoint_block_id
Nothing -> fail $ "Unexpected breakpoint name: " ++ name
verifySimulate ::
(?lc :: Crucible.TypeContext, Crucible.HasPtrWidth wptr, wptr ~ Crucible.ArchWidth arch) =>
Options ->
LLVMCrucibleContext arch ->
[Crucible.GenericExecutionFeature Sym] ->
MS.CrucibleMethodSpecIR (LLVM arch) ->
[(Crucible.MemType, LLVMVal)] ->
[Crucible.LabeledPred Term Crucible.AssumptionReason] ->
W4.ProgramLoc ->
[MS.CrucibleMethodSpecIR (LLVM arch)] ->
Crucible.SymGlobalState Sym ->
Bool ->
Maybe (IORef (Map Text.Text [[Maybe Int]])) ->
IO (Maybe (Crucible.MemType, LLVMVal), Crucible.SymGlobalState Sym)
verifySimulate opts cc pfs mspec args assumes top_loc lemmas globals checkSat asp =
withCfgAndBlockId cc mspec $ \cfg entryId -> do
let argTys = Crucible.blockInputs $
Crucible.getBlock entryId $ Crucible.cfgBlockMap cfg
let retTy = Crucible.handleReturnType $ Crucible.cfgHandle cfg
args' <- prepareArgs argTys (map snd args)
let simCtx = cc^.ccLLVMSimContext
psatf <- Crucible.pathSatisfiabilityFeature sym
(CrucibleSAW.considerSatisfiability sym)
let patSatGenExecFeature = if checkSat then [psatf] else []
when checkSat checkYicesVersion
let (funcLemmas, invLemmas) = partition
(isNothing . view csParentName)
lemmas
breakpoints <- forM (groupOn (view csParentName) invLemmas) $ \specs -> do
let parent = fromJust $ (head specs) ^. csParentName
let breakpoint_names = nubOrd $
map (Crucible.BreakpointName . Text.pack . view csName) specs
withCfg cc parent $ \parent_cfg ->
return
( Crucible.SomeHandle (Crucible.cfgHandle parent_cfg)
, breakpoint_names
)
invariantExecFeatures <- mapM
(registerInvariantOverride opts cc top_loc (HashMap.fromList breakpoints))
(groupOn (view csName) invLemmas)
additionalFeatures <- mapM (Crucible.arraySizeProfile (cc ^. ccLLVMContext))
$ maybeToList asp
let execFeatures = invariantExecFeatures ++
map Crucible.genericToExecutionFeature (patSatGenExecFeature ++ pfs) ++
additionalFeatures
let initExecState =
Crucible.InitialState simCtx globals Crucible.defaultAbortHandler retTy $
Crucible.runOverrideSim retTy $
do mapM_ (registerOverride opts cc simCtx top_loc)
(groupOn (view csName) funcLemmas)
liftIO $ do
preds <- (traverse . Crucible.labeledPred) (resolveSAWPred cc) assumes
Crucible.addAssumptions sym (Seq.fromList preds)
Crucible.regValue <$> (Crucible.callBlock cfg entryId args')
res <- Crucible.executeCrucible execFeatures initExecState
case res of
Crucible.FinishedResult _ partialResult ->
do Crucible.GlobalPair retval globals1 <-
getGlobalPair opts partialResult
let ret_ty = mspec ^. MS.csRet
retval' <- case ret_ty of
Nothing -> return Nothing
Just ret_mt ->
do v <- Crucible.packMemValue sym
(fromMaybe (error ("Expected storable type:" ++ show ret_ty))
(Crucible.toStorableType ret_mt))
(Crucible.regType retval)
(Crucible.regValue retval)
return (Just (ret_mt, v))
return (retval', globals1)
Crucible.TimeoutResult _ -> fail $ "Symbolic execution timed out"
Crucible.AbortedResult _ ar ->
do let resultDoc = ppAbortedResult cc ar
fail $ unlines [ "Symbolic execution failed."
, show resultDoc
]
where
sym = cc^.ccBackend
prepareArgs ::
Ctx.Assignment Crucible.TypeRepr xs ->
[LLVMVal] ->
IO (Crucible.RegMap Sym xs)
prepareArgs ctx x =
Crucible.RegMap <$>
Ctx.traverseWithIndex (\idx tr ->
do v <- Crucible.unpackMemValue sym tr (x !! Ctx.indexVal idx)
return (Crucible.RegEntry tr v))
ctx
-- | Build a conjunction from a list of boolean terms.
scAndList :: SharedContext -> [Term] -> IO Term
scAndList sc = conj . filter nontrivial
where
nontrivial x = asBool x /= Just True
conj [] = scBool sc True
conj (x : xs) = foldM (scAnd sc) x xs
--------------------------------------------------------------------------------
verifyPoststate ::
(?lc :: Crucible.TypeContext, Crucible.HasPtrWidth wptr, wptr ~ Crucible.ArchWidth arch) =>
Options {- ^ saw script debug and print options -} ->
SharedContext {- ^ saw core context -} ->
LLVMCrucibleContext arch {- ^ crucible context -} ->
MS.CrucibleMethodSpecIR (LLVM arch) {- ^ specification -} ->
Map AllocIndex (LLVMPtr wptr) {- ^ allocation substitution -} ->
Crucible.SymGlobalState Sym {- ^ global variables -} ->
Maybe (Crucible.MemType, LLVMVal) {- ^ optional return value -} ->
TopLevel [(String, Term)] {- ^ generated labels and verification conditions -}
verifyPoststate opts sc cc mspec env0 globals ret =
do poststateLoc <- toW4Loc "_SAW_verify_poststate" <$> getPosition
io $ W4.setCurrentProgramLoc sym poststateLoc
let terms0 = Map.fromList
[ (ecVarIndex ec, ttTerm tt)
| tt <- mspec ^. MS.csPreState . MS.csFreshVars
, let Just ec = asExtCns (ttTerm tt) ]
let initialFree = Set.fromList (map (termId . ttTerm)
(view (MS.csPostState . MS.csFreshVars) mspec))
matchPost <- io $
runOverrideMatcher sym globals env0 terms0 initialFree poststateLoc $
do matchResult
learnCond opts sc cc mspec PostState (mspec ^. MS.csPostState)
st <- case matchPost of
Left err -> fail (show err)
Right (_, st) -> return st
io $ for_ (view osAsserts st) $ \(W4.LabeledPred p r) ->
Crucible.addAssertion sym (Crucible.LabeledPred p r)
obligations <- io $ Crucible.getProofObligations sym
io $ Crucible.clearProofObligations sym
io $ mapM verifyObligation (Crucible.proofGoalsToList obligations)
where
sym = cc^.ccBackend
verifyObligation (Crucible.ProofGoal hyps (Crucible.LabeledPred concl (Crucible.SimError _loc err))) = do
hypTerm <- CrucibleSAW.toSC sym =<< W4.andAllOf sym (folded . Crucible.labeledPred) hyps
conclTerm <- CrucibleSAW.toSC sym concl
obligation <- scImplies sc hypTerm conclTerm
return ("safety assertion: " ++ Crucible.simErrorReasonMsg err, obligation)
matchResult =
case (ret, mspec ^. MS.csRetValue) of
(Just (rty,r), Just expect) -> matchArg opts sc cc mspec PostState r rty expect
(Nothing , Just _ ) ->
fail "verifyPoststate: unexpected crucible_return specification"
_ -> return ()
--------------------------------------------------------------------------------
setupLLVMCrucibleContext ::
BuiltinContext ->
Options ->
LLVMModule arch ->
((?lc :: Crucible.TypeContext, Crucible.HasPtrWidth (Crucible.ArchWidth arch)) =>
LLVMCrucibleContext arch -> TopLevel a) ->
TopLevel a
setupLLVMCrucibleContext bic opts lm@(LLVMModule _ llvm_mod mtrans) action = do
halloc <- getHandleAlloc
let ctx = mtrans^.Crucible.transContext
smt_array_memory_model_enabled <- gets rwSMTArrayMemoryModel
Crucible.llvmPtrWidth ctx $ \wptr -> Crucible.withPtrWidth wptr $
let ?lc = ctx^.Crucible.llvmTypeCtx in
action =<< (io $ do
let gen = globalNonceGenerator
let sc = biSharedContext bic