crux-llvm-0.8: src/Crux/LLVM/Overrides.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MagicHash #-}
{-# Language ConstraintKinds #-}
{-# Language DataKinds #-}
{-# Language ImplicitParams #-}
{-# Language LambdaCase #-}
{-# Language PatternSynonyms #-}
{-# Language QuasiQuotes #-}
{-# Language RankNTypes #-}
{-# Language TypeApplications #-}
{-# Language TypeFamilies #-}
{-# Language TypeOperators #-}
{-# Language ViewPatterns #-}
module Crux.LLVM.Overrides
( cruxLLVMOverrides
, svCompOverrides
, cbmcOverrides
, ArchOk
, TPtr
) where
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BS8
import Control.Monad (when)
import Control.Monad.IO.Class(liftIO)
import GHC.Exts ( Proxy# )
import System.IO (hPutStrLn)
import qualified Data.Text as T
import qualified Data.BitVector.Sized as BV
import Data.Parameterized.Context.Unsafe (Assignment)
import Data.Parameterized.Context(pattern Empty, pattern (:>), singleton)
import What4.ProgramLoc( Position(..), ProgramLoc(..) )
import What4.Symbol(userSymbol, emptySymbol, safeSymbol)
import What4.Interface
(freshConstant, bvLit, bvAdd, asBV, predToBV,
getCurrentProgramLoc, printSymExpr, arrayUpdate, bvIsNonzero)
import Lang.Crucible.Types
import Lang.Crucible.CFG.Core(GlobalVar)
import Lang.Crucible.Simulator.RegMap(regValue,RegValue,RegEntry)
import Lang.Crucible.Simulator.ExecutionTree( printHandle )
import Lang.Crucible.Simulator.OverrideSim
( getContext
, readGlobal
, writeGlobal
, ovrWithBackend
)
import Lang.Crucible.Simulator.SimError (SimErrorReason(..),SimError(..))
import Lang.Crucible.Backend
( IsSymInterface, IsSymBackend, addDurableAssertion
, addAssumption, LabeledPred(..), CrucibleAssumption(..)
, backendGetSym
)
import Lang.Crucible.LLVM.QQ( llvmOvr )
import Lang.Crucible.LLVM.DataLayout
(noAlignment)
import Lang.Crucible.LLVM.MemModel
(Mem, LLVMPointerType, loadString, HasPtrWidth,
doMalloc, AllocType(HeapAlloc), Mutability(Mutable),
doArrayStore, doArrayConstStore, HasLLVMAnn,
isAllocatedAlignedPointer, Mutability(..),
pattern PtrWidth, doDumpMem, MemOptions
)
import Lang.Crucible.LLVM.TypeContext( TypeContext )
import Lang.Crucible.LLVM.Intrinsics
import Lang.Crucible.LLVM.Extension ( ArchWidth )
import qualified Crux.Overrides as Crux
import Crux.Types
-- | This happens quite a lot, so just a shorter name
type ArchOk arch = HasPtrWidth (ArchWidth arch)
type TPtr arch = LLVMPointerType (ArchWidth arch)
type TBits n = BVType n
cruxLLVMOverrides ::
( IsSymInterface sym, HasLLVMAnn sym, HasPtrWidth wptr, wptr ~ ArchWidth arch
, ?lc :: TypeContext, ?intrinsicsOpts :: IntrinsicsOptions, ?memOpts :: MemOptions ) =>
Proxy# arch ->
[OverrideTemplate (personality sym) sym arch rtp l a]
cruxLLVMOverrides arch =
[ basic_llvm_override $
[llvmOvr| i8 @crucible_int8_t( i8* ) |]
(fresh_bits arch (knownNat @8))
, basic_llvm_override $
[llvmOvr| i16 @crucible_int16_t( i8* ) |]
(fresh_bits arch (knownNat @16))
, basic_llvm_override $
[llvmOvr| i32 @crucible_int32_t( i8* ) |]
(fresh_bits arch (knownNat @32))
, basic_llvm_override $
[llvmOvr| i64 @crucible_int64_t( i8* ) |]
(fresh_bits arch (knownNat @64))
, basic_llvm_override $
[llvmOvr| i8 @crucible_uint8_t( i8* ) |]
(fresh_bits arch (knownNat @8))
, basic_llvm_override $
[llvmOvr| i16 @crucible_uint16_t( i8* ) |]
(fresh_bits arch (knownNat @16))
, basic_llvm_override $
[llvmOvr| i32 @crucible_uint32_t( i8* ) |]
(fresh_bits arch (knownNat @32))
, basic_llvm_override $
[llvmOvr| i64 @crucible_uint64_t( i8* ) |]
(fresh_bits arch (knownNat @64))
, basic_llvm_override $
[llvmOvr| float @crucible_float( i8* ) |]
(fresh_float arch SingleFloatRepr)
, basic_llvm_override $
[llvmOvr| double @crucible_double( i8* ) |]
(fresh_float arch DoubleFloatRepr)
, basic_llvm_override $
[llvmOvr| i8* @crucible_string( i8*, size_t ) |]
(fresh_str arch)
, basic_llvm_override $
[llvmOvr| void @crucible_assume( i8, i8*, i32 ) |]
(do_assume arch)
, basic_llvm_override $
[llvmOvr| void @crucible_assert( i8, i8*, i32 ) |]
(do_assert arch)
, basic_llvm_override $
[llvmOvr| void @crucible_print_uint32( i32 ) |]
do_print_uint32
, basic_llvm_override $
[llvmOvr| void @crucible_havoc_memory( i8*, size_t ) |]
(do_havoc_memory arch)
, basic_llvm_override $
[llvmOvr| void @crucible_dump_memory( ) |]
(\mvar _sym _args ->
do mem <- readGlobal mvar
h <- printHandle <$> getContext
liftIO (doDumpMem h mem))
]
cbmcOverrides ::
( IsSymInterface sym, HasLLVMAnn sym, HasPtrWidth wptr, wptr ~ ArchWidth arch
, ?lc :: TypeContext, ?intrinsicsOpts :: IntrinsicsOptions, ?memOpts :: MemOptions ) =>
Proxy# arch ->
[OverrideTemplate (personality sym) sym arch rtp l a]
cbmcOverrides arch =
[ basic_llvm_override $
[llvmOvr| void @__CPROVER_assume( i32 ) |]
cprover_assume
, basic_llvm_override $
[llvmOvr| void @__CPROVER_assert( i32, i8* ) |]
(cprover_assert arch)
, basic_llvm_override $
[llvmOvr| i1 @__CPROVER_r_ok( i8*, size_t ) |]
(cprover_r_ok arch)
, basic_llvm_override $
[llvmOvr| i1 @__CPROVER_w_ok( i8*, size_t ) |]
(cprover_w_ok arch)
, basic_llvm_override $
[llvmOvr| i1 @nondet_bool() |]
(sv_comp_fresh_bits (knownNat @1))
, basic_llvm_override $
[llvmOvr| i8 @nondet_char() |]
(sv_comp_fresh_bits (knownNat @8))
, basic_llvm_override $
[llvmOvr| i8 @nondet_uchar() |]
(sv_comp_fresh_bits (knownNat @8))
, basic_llvm_override $
[llvmOvr| i8 @nondet_uint8_t() |]
(sv_comp_fresh_bits (knownNat @8))
, basic_llvm_override $
[llvmOvr| i8 @nondet_int8_t() |]
(sv_comp_fresh_bits (knownNat @8))
, basic_llvm_override $
[llvmOvr| i16 @nondet_short() |]
(sv_comp_fresh_bits (knownNat @16))
, basic_llvm_override $
[llvmOvr| i16 @nondet_ushort() |]
(sv_comp_fresh_bits (knownNat @16))
, basic_llvm_override $
[llvmOvr| i16 @nondet_int16_t() |]
(sv_comp_fresh_bits (knownNat @16))
, basic_llvm_override $
[llvmOvr| i16 @nondet_uint16_t() |]
(sv_comp_fresh_bits (knownNat @16))
, basic_llvm_override $
[llvmOvr| i32 @nondet_int() |]
(sv_comp_fresh_bits (knownNat @32))
, basic_llvm_override $
[llvmOvr| i32 @nondet_uint() |]
(sv_comp_fresh_bits (knownNat @32))
, basic_llvm_override $
[llvmOvr| i32 @nondet_int32_t() |]
(sv_comp_fresh_bits (knownNat @32))
, basic_llvm_override $
[llvmOvr| i32 @nondet_uint32_t() |]
(sv_comp_fresh_bits (knownNat @32))
, basic_llvm_override $
[llvmOvr| i64 @nondet_int64_t() |]
(sv_comp_fresh_bits (knownNat @64))
, basic_llvm_override $
[llvmOvr| i64 @nondet_uint64_t() |]
(sv_comp_fresh_bits (knownNat @64))
-- @nondet_long@ returns a `long`, so we need two overrides for
-- @nondet_long@. Similarly for @nondet_ulong@.
-- See Note [Overrides involving (unsigned) long] in
-- crucible-llvm:Lang.Crucible.LLVM.Intrinsics.
, basic_llvm_override $
[llvmOvr| i32 @nondet_long() |]
(sv_comp_fresh_bits (knownNat @32))
, basic_llvm_override $
[llvmOvr| i64 @nondet_long() |]
(sv_comp_fresh_bits (knownNat @64))
, basic_llvm_override $
[llvmOvr| i32 @nondet_ulong() |]
(sv_comp_fresh_bits (knownNat @32))
, basic_llvm_override $
[llvmOvr| i64 @nondet_ulong() |]
(sv_comp_fresh_bits (knownNat @64))
, basic_llvm_override $
[llvmOvr| size_t @nondet_size_t() |]
(sv_comp_fresh_bits ?ptrWidth)
, basic_llvm_override $
[llvmOvr| float @nondet_float() |]
(sv_comp_fresh_float SingleFloatRepr)
, basic_llvm_override $
[llvmOvr| double @nondet_double() |]
(sv_comp_fresh_float DoubleFloatRepr)
{-
, basic_llvm_override $
[llvmOvr| i8* @nondet_voidp() |]
(sv_comp_fresh_bits ?ptrWidth)
-}
]
svCompOverrides ::
(IsSymInterface sym, HasLLVMAnn sym, HasPtrWidth wptr) =>
[OverrideTemplate (personality sym) sym arch rtp l a]
svCompOverrides =
[ basic_llvm_override $
[llvmOvr| i64 @__VERIFIER_nondet_longlong() |]
(sv_comp_fresh_bits (knownNat @64))
-- loff_t is pretty Linux-specific, so we can't point to the C or POSIX
-- standards for justification about what size it should be. The man page
-- for lseek64 (https://linux.die.net/man/3/lseek64) is as close to a
-- definitive reference for this as I can find, which says
-- "The type loff_t is a 64-bit signed type".
, basic_llvm_override $
[llvmOvr| i64 @__VERIFIER_nondet_loff_t() |]
(sv_comp_fresh_bits (knownNat @64))
-- @__VERIFIER_nondet_ulong@ returns an `unsigned long`, so we need two
-- overrides for @__VERIFIER_nondet_ulong@. Similarly for
-- @__VERIFIER_nondet_long@. See Note [Overrides involving (unsigned) long]
-- in crucible-llvm:Lang.Crucible.LLVM.Intrinsics.
, basic_llvm_override $
[llvmOvr| i32 @__VERIFIER_nondet_ulong() |]
(sv_comp_fresh_bits (knownNat @32))
, basic_llvm_override $
[llvmOvr| i64 @__VERIFIER_nondet_ulong() |]
(sv_comp_fresh_bits (knownNat @64))
, basic_llvm_override $
[llvmOvr| i32 @__VERIFIER_nondet_long() |]
(sv_comp_fresh_bits (knownNat @32))
, basic_llvm_override $
[llvmOvr| i64 @__VERIFIER_nondet_long() |]
(sv_comp_fresh_bits (knownNat @64))
, basic_llvm_override $
[llvmOvr| i32 @__VERIFIER_nondet_unsigned() |]
(sv_comp_fresh_bits (knownNat @32))
, basic_llvm_override $
[llvmOvr| i32 @__VERIFIER_nondet_u32() |]
(sv_comp_fresh_bits (knownNat @32))
, basic_llvm_override $
[llvmOvr| i32 @__VERIFIER_nondet_uint() |]
(sv_comp_fresh_bits (knownNat @32))
, basic_llvm_override $
[llvmOvr| i32 @__VERIFIER_nondet_int() |]
(sv_comp_fresh_bits (knownNat @32))
, basic_llvm_override $
[llvmOvr| i16 @__VERIFIER_nondet_u16() |]
(sv_comp_fresh_bits (knownNat @16))
, basic_llvm_override $
[llvmOvr| i16 @__VERIFIER_nondet_ushort() |]
(sv_comp_fresh_bits (knownNat @16))
, basic_llvm_override $
[llvmOvr| i16 @__VERIFIER_nondet_short() |]
(sv_comp_fresh_bits (knownNat @16))
, basic_llvm_override $
[llvmOvr| i8 @__VERIFIER_nondet_u8() |]
(sv_comp_fresh_bits (knownNat @8))
, basic_llvm_override $
[llvmOvr| i8 @__VERIFIER_nondet_uchar() |]
(sv_comp_fresh_bits (knownNat @8))
, basic_llvm_override $
[llvmOvr| i8 @__VERIFIER_nondet_char() |]
(sv_comp_fresh_bits (knownNat @8))
, basic_llvm_override $
[llvmOvr| i1 @__VERIFIER_nondet_bool() |]
(sv_comp_fresh_bits (knownNat @1))
, basic_llvm_override $
[llvmOvr| size_t @__VERIFIER_nondet_size_t() |]
(sv_comp_fresh_bits ?ptrWidth)
, basic_llvm_override $
[llvmOvr| float @__VERIFIER_nondet_float() |]
(sv_comp_fresh_float SingleFloatRepr)
, basic_llvm_override $
[llvmOvr| double @__VERIFIER_nondet_double() |]
(sv_comp_fresh_float DoubleFloatRepr)
]
--------------------------------------------------------------------------------
lookupString ::
( IsSymInterface sym, HasLLVMAnn sym, ArchOk arch
, ?memOpts :: MemOptions ) =>
Proxy# arch ->
GlobalVar Mem -> RegEntry sym (TPtr arch) -> OverM personality sym ext String
lookupString _ mvar ptr =
ovrWithBackend $ \bak ->
do mem <- readGlobal mvar
bytes <- liftIO (loadString bak mem (regValue ptr) Nothing)
return (BS8.unpack (BS.pack bytes))
sv_comp_fresh_bits ::
(IsSymBackend sym bak, 1 <= w) =>
NatRepr w ->
GlobalVar Mem ->
bak ->
Assignment (RegEntry sym) EmptyCtx ->
OverM personality sym ext (RegValue sym (BVType w))
sv_comp_fresh_bits w _mvar _bak Empty = Crux.mkFresh (safeSymbol "X") (BaseBVRepr w)
sv_comp_fresh_float ::
(IsSymBackend sym bak) =>
FloatInfoRepr fi ->
GlobalVar Mem ->
bak ->
Assignment (RegEntry sym) EmptyCtx ->
OverM personality sym ext (RegValue sym (FloatType fi))
sv_comp_fresh_float fi _mvar _bak Empty = Crux.mkFreshFloat (safeSymbol "X") fi
fresh_bits ::
( ArchOk arch, HasLLVMAnn sym, IsSymBackend sym bak, 1 <= w
, ?memOpts :: MemOptions ) =>
Proxy# arch ->
NatRepr w ->
GlobalVar Mem ->
bak ->
Assignment (RegEntry sym) (EmptyCtx ::> TPtr arch) ->
OverM personality sym ext (RegValue sym (BVType w))
fresh_bits arch w mvar _ (Empty :> pName) =
do name <- lookupString arch mvar pName
Crux.mkFresh (safeSymbol name) (BaseBVRepr w)
fresh_float ::
( ArchOk arch, IsSymBackend sym bak, HasLLVMAnn sym
, ?memOpts :: MemOptions ) =>
Proxy# arch ->
FloatInfoRepr fi ->
GlobalVar Mem ->
bak ->
Assignment (RegEntry sym) (EmptyCtx ::> TPtr arch) ->
OverM personality sym ext (RegValue sym (FloatType fi))
fresh_float arch fi mvar _ (Empty :> pName) =
do name <- lookupString arch mvar pName
Crux.mkFreshFloat (safeSymbol name) fi
fresh_str ::
( ArchOk arch, IsSymBackend sym bak, HasLLVMAnn sym
, ?memOpts :: MemOptions ) =>
Proxy# arch ->
GlobalVar Mem ->
bak ->
Assignment (RegEntry sym) (EmptyCtx ::> TPtr arch ::> BVType (ArchWidth arch)) ->
OverM personality sym ext (RegValue sym (TPtr arch))
fresh_str arch mvar bak (Empty :> pName :> maxLen) =
do let sym = backendGetSym bak
name <- lookupString arch mvar pName
-- Compute the allocation length, which is the requested length plus one
-- to hold the NUL terminator
one <- liftIO $ bvLit sym ?ptrWidth (BV.one ?ptrWidth)
-- maxLenBV <- liftIO $ projectLLVM_bv sym (regValue maxLen)
len <- liftIO $ bvAdd sym (regValue maxLen) one
mem0 <- readGlobal mvar
-- Allocate memory to hold the string
(ptr, mem1) <- liftIO $ doMalloc bak HeapAlloc Mutable name mem0 len noAlignment
-- Allocate contents for the string - we want to make each byte symbolic,
-- so we allocate a fresh array (which has unbounded length) with symbolic
-- contents and write it into our allocation. This write does not cover
-- the NUL terminator.
contentsName <- case userSymbol (name ++ "_contents") of
Left err -> fail (show err)
Right nm -> return nm
let arrayRep = BaseArrayRepr (Empty :> BaseBVRepr ?ptrWidth) (BaseBVRepr (knownNat @8))
initContents <- liftIO $ freshConstant sym contentsName arrayRep
zeroByte <- liftIO $ bvLit sym (knownNat @8) (BV.zero knownNat)
-- Put the NUL terminator in place
initContentsZ <- liftIO $ arrayUpdate sym initContents (singleton (regValue maxLen)) zeroByte
mem2 <- liftIO $ doArrayConstStore bak mem1 ptr noAlignment initContentsZ len
writeGlobal mvar mem2
return ptr
do_assume ::
( ArchOk arch, IsSymBackend sym bak, HasLLVMAnn sym
, ?memOpts :: MemOptions ) =>
Proxy# arch ->
GlobalVar Mem ->
bak ->
Assignment (RegEntry sym) (EmptyCtx ::> TBits 8 ::> TPtr arch ::> TBits 32) ->
OverM personality sym ext (RegValue sym UnitType)
do_assume arch mvar bak (Empty :> p :> pFile :> line) =
do let sym = backendGetSym bak
cond <- liftIO $ bvIsNonzero sym (regValue p)
file <- lookupString arch mvar pFile
l <- case asBV (regValue line) of
Just (BV.BV l) -> return (fromInteger l)
Nothing -> return 0
let pos = SourcePos (T.pack file) l 0
loc <- liftIO $ getCurrentProgramLoc sym
let loc' = loc{ plSourceLoc = pos }
liftIO $ addAssumption bak (GenericAssumption loc' "crucible_assume" cond)
do_assert ::
( ArchOk arch, IsSymBackend sym bak, HasLLVMAnn sym
, ?intrinsicsOpts :: IntrinsicsOptions, ?memOpts :: MemOptions ) =>
Proxy# arch ->
GlobalVar Mem ->
bak ->
Assignment (RegEntry sym) (EmptyCtx ::> TBits 8 ::> TPtr arch ::> TBits 32) ->
OverM personality sym ext (RegValue sym UnitType)
do_assert arch mvar bak (Empty :> p :> pFile :> line) =
when (abnormalExitBehavior ?intrinsicsOpts == AlwaysFail) $
do let sym = backendGetSym bak
cond <- liftIO $ bvIsNonzero sym (regValue p)
file <- lookupString arch mvar pFile
l <- case asBV (regValue line) of
Just (BV.BV l) -> return (fromInteger l)
Nothing -> return 0
let pos = SourcePos (T.pack file) l 0
loc <- liftIO $ getCurrentProgramLoc sym
let loc' = loc{ plSourceLoc = pos }
let msg = GenericSimError "crucible_assert"
liftIO $ addDurableAssertion bak (LabeledPred cond (SimError loc' msg))
do_print_uint32 ::
(IsSymBackend sym bak) =>
GlobalVar Mem ->
bak ->
Assignment (RegEntry sym) (EmptyCtx ::> TBits 32) ->
OverM personality sym ext (RegValue sym UnitType)
do_print_uint32 _mvar _bak (Empty :> x) =
do h <- printHandle <$> getContext
liftIO $ hPutStrLn h (show (printSymExpr (regValue x)))
do_havoc_memory ::
(ArchOk arch, IsSymBackend sym bak, HasLLVMAnn sym) =>
Proxy# arch ->
GlobalVar Mem ->
bak ->
Assignment (RegEntry sym) (EmptyCtx ::> TPtr arch ::> TBits (ArchWidth arch)) ->
OverM personality sym ext (RegValue sym UnitType)
do_havoc_memory _ mvar bak (Empty :> ptr :> len) =
do let sym = backendGetSym bak
let tp = BaseArrayRepr (Empty :> BaseBVRepr ?ptrWidth) (BaseBVRepr (knownNat @8))
mem <- readGlobal mvar
mem' <- liftIO $ do
arr <- freshConstant sym emptySymbol tp
doArrayStore bak mem (regValue ptr) noAlignment arr (regValue len)
writeGlobal mvar mem'
cprover_assume ::
(IsSymBackend sym bak) =>
GlobalVar Mem ->
bak ->
Assignment (RegEntry sym) (EmptyCtx ::> TBits 32) ->
OverM personality sym ext (RegValue sym UnitType)
cprover_assume _mvar bak (Empty :> p) = liftIO $
do let sym = backendGetSym bak
cond <- bvIsNonzero sym (regValue p)
loc <- getCurrentProgramLoc sym
addAssumption bak (GenericAssumption loc "__CPROVER_assume" cond)
cprover_assert ::
( ArchOk arch, IsSymBackend sym bak, HasLLVMAnn sym
, ?intrinsicsOpts :: IntrinsicsOptions, ?memOpts :: MemOptions ) =>
Proxy# arch ->
GlobalVar Mem ->
bak ->
Assignment (RegEntry sym) (EmptyCtx ::> TBits 32 ::> TPtr arch) ->
OverM personality sym ext (RegValue sym UnitType)
cprover_assert arch mvar bak (Empty :> p :> pMsg) =
when (abnormalExitBehavior ?intrinsicsOpts == AlwaysFail) $
do let sym = backendGetSym bak
cond <- liftIO $ bvIsNonzero sym (regValue p)
str <- lookupString arch mvar pMsg
loc <- liftIO $ getCurrentProgramLoc sym
let msg = AssertFailureSimError "__CPROVER_assert" str
liftIO $ addDurableAssertion bak (LabeledPred cond (SimError loc msg))
cprover_r_ok ::
(ArchOk arch, IsSymBackend sym bak, HasLLVMAnn sym) =>
Proxy# arch ->
GlobalVar Mem ->
bak ->
Assignment (RegEntry sym) (EmptyCtx ::> TPtr arch ::> BVType (ArchWidth arch)) ->
OverM personality sym ext (RegValue sym (BVType 1))
cprover_r_ok _ mvar bak (Empty :> (regValue -> p) :> (regValue -> sz)) =
do let sym = backendGetSym bak
mem <- readGlobal mvar
x <- liftIO $ isAllocatedAlignedPointer sym PtrWidth noAlignment Immutable p (Just sz) mem
liftIO $ predToBV sym x knownNat
cprover_w_ok ::
(ArchOk arch, IsSymBackend sym bak, HasLLVMAnn sym) =>
Proxy# arch ->
GlobalVar Mem ->
bak ->
Assignment (RegEntry sym) (EmptyCtx ::> TPtr arch ::> BVType (ArchWidth arch)) ->
OverM personality sym ext (RegValue sym (BVType 1))
cprover_w_ok _ mvar bak (Empty :> (regValue -> p) :> (regValue -> sz)) =
do let sym = backendGetSym bak
mem <- readGlobal mvar
x <- liftIO $ isAllocatedAlignedPointer sym PtrWidth noAlignment Mutable p (Just sz) mem
liftIO $ predToBV sym x knownNat