Fixes for jump table tests.

* Update macaw-x86-tests to build properly.
* Fix off by two error in memMapOverwrite
* Introduce some special handling for unsigned-extension in stack
  analysis so it knows one value is the unsigned extension of another.
* Error report formating improvements
* Slightly more precise treatment of archfn is bound updates.
This commit is contained in:
Joe Hendrix 2020-11-12 11:25:30 -08:00
parent 5aad8ca32e
commit d2b81d3c2f
8 changed files with 389 additions and 116 deletions

View File

@ -36,8 +36,9 @@ module Data.Macaw.AbsDomain.JumpBounds
import Control.Monad.Reader
import Data.Bits
import Data.Foldable
import qualified Data.Map.Strict as Map
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Monoid
import Data.Parameterized.Classes
import Data.Parameterized.Map (MapF)
import qualified Data.Parameterized.Map as MapF
@ -67,6 +68,7 @@ data RangePred u =
, rangeLowerBound :: !Natural
, rangeUpperBound :: !Natural
}
deriving (Show)
mkLowerBound :: NatRepr u -> Natural -> RangePred u
mkLowerBound w l = RangePred w l (fromInteger (maxUnsigned w))
@ -140,7 +142,10 @@ functionStartBounds =
-- | Return a jump bounds that implies both input bounds, or `Nothing`
-- if every fact in the old bounds is implied by the new bounds.
joinInitialBounds :: forall arch
. (MemWidth (ArchAddrWidth arch), OrdF (ArchReg arch))
. ( MemWidth (ArchAddrWidth arch)
, OrdF (ArchReg arch)
, HasRepr (ArchReg arch) TypeRepr
)
=> InitJumpBounds arch
-> InitJumpBounds arch
-> Maybe (InitJumpBounds arch)
@ -168,7 +173,7 @@ data IntraJumpBounds arch ids
, intraMemCleared :: !Bool
-- ^ Flag to indicate if we had any memory
-- writes in block that could have altered
-- invariants on memory accesses.
-- invariants on non-stack memory accesses.
}
-- | Create index bounds from initial index bounds.
@ -194,18 +199,36 @@ modifyIntraStackConstraints ::IntraJumpBounds arch ids
modifyIntraStackConstraints bnds f = bnds { intraStackConstraints = f (intraStackConstraints bnds) }
discardMemBounds :: IntraJumpBounds arch ids -> IntraJumpBounds arch ids
discardMemBounds bnds = bnds { intraStackConstraints = discardMemInfo (intraStackConstraints bnds)
, intraMemCleared = True
}
discardMemBounds bnds =
bnds { intraStackConstraints = discardMemInfo (intraStackConstraints bnds)
, intraMemCleared = True
}
------------------------------------------------------------------------
-- Execute a statement
-- | Function that determines whether it thinks an
-- architecture-specific function could modify stack.
--
-- Uses principal that architecture-specific functions can only depend
-- on value if they reference it in their foldable instance.
archFnCouldAffectStack :: forall f arch ids tp
. (FoldableFC f, MemWidth (ArchAddrWidth arch), OrdF (ArchReg arch))
=> BlockIntraStackConstraints arch ids
-> f (Value arch ids) tp
-> Bool
archFnCouldAffectStack cns = getAny . foldMapFC (Any . refStack)
where refStack :: Value arch ids utp -> Bool
refStack v =
case intraStackValueExpr cns v of
StackOffsetExpr _ -> True
_ -> False
-- | Given a statement this modifies the bounds based on the statement.
execStatement :: ( MemWidth (ArchAddrWidth arch)
, OrdF (ArchReg arch)
, ShowF (ArchReg arch)
, FoldableFC (ArchFn arch)
)
=> IntraJumpBounds arch ids
-> Stmt arch ids
@ -213,11 +236,6 @@ execStatement :: ( MemWidth (ArchAddrWidth arch)
execStatement bnds stmt =
case stmt of
AssignStmt (Assignment aid arhs) -> do
-- Clear all knowledge about the stack on architecture-specific
-- functions as they may have side effects.
--
-- Note. This is very conservative, and we may need to improve
-- this.
let bnds1 = case arhs of
ReadMem addrVal readRepr
| False <- intraMemCleared bnds
@ -225,15 +243,25 @@ execStatement bnds stmt =
, Just (MemVal bndRepr bnd) <- Map.lookup addr (initAddrPredMap (intraInitBounds bnds))
, Just Refl <- testEquality readRepr bndRepr ->
bnds { intraReadPredMap = MapF.insert aid bnd (intraReadPredMap bnds) }
EvalArchFn{} -> discardMemBounds bnds
-- Clear all knowledge about the stack on architecture-specific
-- functions that accept stack pointer as they may have side effects.
--
-- Note. This is very conservative, and we may need to improve
-- this.
EvalArchFn f _ ->
if archFnCouldAffectStack (intraStackConstraints bnds) f then
discardMemBounds bnds
else
bnds { intraMemCleared = True }
_ -> bnds
-- Associate the given expression with a bounds.
in modifyIntraStackConstraints bnds1 $ \cns ->
intraStackSetAssignId aid (intraStackRhsExpr cns aid arhs) cns
WriteMem addr repr val ->
case intraStackValueExpr (intraStackConstraints bnds) addr of
StackOffsetExpr addrOff -> modifyIntraStackConstraints bnds $ \cns ->
writeStackOff cns addrOff repr val
StackOffsetExpr addrOff ->
modifyIntraStackConstraints bnds $ \cns ->
writeStackOff cns addrOff repr val
-- If we write to something other than stack, then clear all
-- stack references.
--
@ -243,14 +271,15 @@ execStatement bnds stmt =
CondWriteMem{} -> discardMemBounds bnds
InstructionStart{} -> bnds
Comment{} -> bnds
ExecArchStmt{} ->modifyIntraStackConstraints bnds discardMemInfo
ExecArchStmt{} -> discardMemBounds bnds
ArchState{} -> bnds
-- | Create bounds for end of block.
blockEndBounds :: ( MemWidth (ArchAddrWidth arch)
, OrdF (ArchReg arch)
, ShowF (ArchReg arch)
)
, OrdF (ArchReg arch)
, ShowF (ArchReg arch)
, FoldableFC (ArchFn arch)
)
=> InitJumpBounds arch
-> [Stmt arch ids] -- ^
-> IntraJumpBounds arch ids
@ -308,6 +337,21 @@ exprRangePred info e = do
, rangeNotTotal r ->
SomeRangePred r
-- Otherwise see what we can infer.
UExtExpr x w ->
case exprRangePred info x of
NoRangePred -> SomeRangePred (mkRangeBound w 0 (fromInteger (maxUnsigned w)))
SomeRangePred r ->
-- If bound covers all the bits in x, then we can extend it to all
-- the bits in result (since new bits are false)
--
-- Otherwise, we only have the partial bound
if leqF (typeWidth x) (rangeWidth r) then
SomeRangePred (mkRangeBound w (rangeLowerBound r) (rangeUpperBound r))
else
-- Dynamic check on range width that should never fail.
case testLeq (rangeWidth r) w of
Just LeqProof -> SomeRangePred r
Nothing -> error "exprRangePred given malformed app."
AppExpr _n app ->
case app of
UExt x w
@ -538,6 +582,7 @@ addRangePred :: ( MapF.OrdF (ArchReg arch)
, HasRepr (ArchReg arch) TypeRepr
, u <= w
, MemWidth (ArchAddrWidth arch)
, ShowF (ArchReg arch)
)
=> BlockIntraStackConstraints arch ids
-> StackExpr arch ids (BVType w)
@ -580,7 +625,15 @@ addRangePred cns v p =
pure $! emptyBranchConstraints
SymbolCValue{} ->
pure $! emptyBranchConstraints
UExtExpr x _outWidth
-- If this constraint affects fewer bits than the extension,
-- then we just propagate the property to value
-- pre-extension.
| Just LeqProof <- testLeq (rangeWidth p) (typeWidth x) ->
addRangePred cns x p
-- Otherwise, we still can constrain our width.
| otherwise -> do
addRangePred cns x (p { rangeWidth = typeWidth x })
AppExpr n a ->
case a of
BVAdd _ x (CExpr (BVCValue w c))
@ -592,16 +645,6 @@ addRangePred cns v p =
addRangePred cns x (RangePred w (fromInteger (toUnsigned w l'))
(fromInteger (toUnsigned w u')))
UExt x _outWidth
-- If this constraint affects fewer bits than the extension,
-- then we just propagate the property to value
-- pre-extension.
| Just LeqProof <- testLeq (rangeWidth p) (typeWidth x) ->
addRangePred cns x p
-- Otherwise, we still can constraint our width,
| RangePred _ l u <- p -> do
LeqProof <- pure (leqRefl (typeWidth x))
addRangePred cns x (RangePred (typeWidth x) l u)
-- Truncation passes through as we aonly affect low order bits.
Trunc x _w ->
case testLeq (rangeWidth p) (typeWidth x) of

View File

@ -7,7 +7,33 @@ domain also tracks equalities between nodes so that analysis
algorithms built on top of this are modulo known equivalences
between registers and stack locations.
The theory is defined over bitvector with a predefined
"address width" @addrWidth@ for the number of bits in the
address space
> p := t = u
> | t = stack_frame + c
> | t = uext u w (1 <= width u && width u < w)
>
> t := r (@r@ is a register)
> | *(stack_frame + c)
@c@ is a signed @addrWidth@-bit bitvector. @stack_frame@ denotes
a @addrWidth@-bit bitvector for the stack frame. @w@ is a width
parameter.
Given a set of axioms using the constraint @P@ we have the following
proof rules:
> A,p |= p (assumption)
> A |= t = t (reflexivity)
> A |= t = u => A |= u = t (symmetry)
> A |= t = u && A |= u = v => A |= t = v (transitivity)
> A |= t = uext u w && A |= u = uext v w' => A |= t = uext v w (double uext)
> A |= t = uext u w && A |= u = v => A |= t = uext v w (uext congruence)
-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
@ -15,6 +41,8 @@ between registers and stack locations.
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Macaw.AbsDomain.StackAnalysis
( -- * Block level datatypes
@ -77,16 +105,17 @@ module Data.Macaw.AbsDomain.StackAnalysis
import Control.Monad.Reader
import Control.Monad.State
import Data.Functor
import Data.Kind
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Parameterized.Classes
import Data.Parameterized.Map (MapF)
import qualified Data.Parameterized.Map as MapF
import Data.Parameterized.NatRepr
import Data.Parameterized.Pair
import Data.Parameterized.TraversableF
import Data.Parameterized.TraversableFC
import Data.Proxy
import Data.STRef
import GHC.Natural
import Text.PrettyPrint.ANSI.Leijen hiding ((<$>))
@ -193,11 +222,17 @@ instance ShowF r => Show (BoundLoc r tp) where
instance ShowF r => ShowF (BoundLoc r)
instance TestEquality r => Eq (BoundLoc r tp) where
x == y = isJust (testEquality x y)
------------------------------------------------------------------------
-- MemMap
-- | This is a data structure for representing values written to
-- concrete stack offsets.
-- concrete offsets in memory.
--
-- The offset type is a parameter so that we can use signed or
-- unsigned offsets.
newtype MemMap o (v :: M.Type -> Type) = SM (Map o (MemVal v))
instance FunctorF (MemMap o) where
@ -283,11 +318,12 @@ memMapOverwrite :: forall o p tp
-> MemMap o p
memMapOverwrite off repr v (SM m)
| e <= off = error "stackOverwrite fail"
| memReprBytes repr == 0 = SM m
| otherwise =
let (lm, _) = Map.split off m
(_, hm) = Map.split e m
in SM $ Map.insert off (MemVal repr v) lm <> hm
where e = off + fromIntegral (memReprBytes repr) + 1
where e = off + fromIntegral (memReprBytes repr) - 1
-- | This sets the value at an offset without checking to clear any
-- previous writes to values.
@ -452,14 +488,22 @@ data StackEqConstraint r tp where
-- As stacks grow down on many architectures, the offset will
-- typically be negative.
IsStackOff :: !(MemInt (RegAddrWidth r)) -> StackEqConstraint r (BVType (RegAddrWidth r))
-- | This indicates the value is equal to the value stored at the
-- | Indicates the value is equal to the value stored at the
-- location.
EqualLoc :: !(BoundLoc r tp) -> StackEqConstraint r tp
-- | Indicates the value is the unsigned extension of the value
-- at another location.
IsUExt :: (1 <= u, u+1 <= w)
=> !(BoundLoc r (BVType u))
-> !(NatRepr w)
-> StackEqConstraint r (BVType w)
ppStackEqConstraint :: ShowF r => Doc -> StackEqConstraint r tp -> Doc
ppStackEqConstraint d (IsStackOff i) =
d <+> text "= stack_frame" <+> ppAddend i
ppStackEqConstraint d (EqualLoc l) = d <+> text "=" <+> pretty l
ppStackEqConstraint d (EqualLoc l) = d <> text " = " <> pretty l
ppStackEqConstraint d (IsUExt l w) = d <> text " = (uext " <> pretty l <+> text (show w) <> text ")"
------------------------------------------------------------------------
-- BlockStartStackConstraints
@ -488,9 +532,10 @@ fnEntryBlockStartStackConstraints =
}
}
-- | @blockStartLocStackOffset c l@ returns an offset of the stack
-- pointer that the value stored in @l@ is inferred to be equivalent
-- to when the block starts execution.
-- | @blockStartLocStackOffset c l@ determines if @l@ has the form @stack_frame + o@
-- using the constraints @c@ for some stack offset @o@.
--
-- If it does, then this returns @Just o@. Otherwise it returns @Nothing@.
blockStartLocStackOffset :: (MemWidth (ArchAddrWidth arch), OrdF (ArchReg arch))
=> BlockStartStackConstraints arch
-> BoundLoc (ArchReg arch) tp
@ -500,27 +545,43 @@ blockStartLocStackOffset cns loc =
Nothing -> Nothing
Just (IsStackOff o) -> Just o
Just (EqualLoc loc') -> blockStartLocStackOffset cns loc'
-- Unsigned extensions cannot be stack offsets.
Just (IsUExt _ _) -> Nothing
data StackOffConstraint w tp where
StackOffCns :: !(MemInt w) -> StackOffConstraint w (BVType w)
-- | Constraint on a class representative
data StackOffConstraint r tp where
StackOffCns :: !(MemInt (RegAddrWidth r)) -> StackOffConstraint r (BVType (RegAddrWidth r))
UExtCns :: (1 <= u, u+1 <= w)
=> !(BoundLoc r (BVType u))
-> !(NatRepr w)
-> StackOffConstraint r (BVType w)
instance TestEquality r => Eq (StackOffConstraint r tp) where
StackOffCns i == StackOffCns j = i == j
UExtCns xl _ == UExtCns yl _ =
case testEquality xl yl of
Just Refl -> True
Nothing -> False
_ == _ = False
-- | @boundsLocRep cns loc@ returns the representative location for
-- @loc@.
--
-- This representative location has the property that a location must
-- have the same value as its representative location, and if two
-- locations have provably equal values in the bounds, then they must
-- locations have provably equal values in @cns@, then they must
-- have the same representative.
blockStartLocRepAndCns :: (MemWidth (ArchAddrWidth arch), OrdF (ArchReg arch))
=> BlockStartStackConstraints arch
-> BoundLoc (ArchReg arch) tp
-> ( BoundLoc (ArchReg arch) tp
, Maybe (StackOffConstraint (ArchAddrWidth arch) tp)
, Maybe (StackOffConstraint (ArchReg arch) tp)
)
blockStartLocRepAndCns cns l =
case locLookup l (unBSSC cns) of
Just (EqualLoc loc) -> blockStartLocRepAndCns cns loc
Just (IsStackOff o) -> (l,Just (StackOffCns o))
Just (IsStackOff o) -> (l, Just (StackOffCns o))
Just (IsUExt loc w) -> (l, Just (UExtCns loc w))
Nothing -> (l, Nothing)
------------------------------------------------------------------------
@ -530,68 +591,140 @@ blockStartLocRepAndCns cns l =
-- representative in merged map.
type JoinClassMap r = MapF (JoinClassPair (BoundLoc r)) (BoundLoc r)
joinStackEqConstraint :: Maybe (StackOffConstraint w tp)
-> Maybe (StackOffConstraint w tp)
-> Changed s (Maybe (StackOffConstraint w tp))
joinStackEqConstraint Nothing _ = pure Nothing
joinStackEqConstraint p@(Just (StackOffCns i)) (Just (StackOffCns j)) | i == j = pure p
joinStackEqConstraint _ _ = markChanged True $> Nothing
-- | Information needed to join classes.
data JoinContext s arch = JoinContext { jctxOldCns :: !(BlockStartStackConstraints arch)
-- ^ Old constraints being joined
, jctxNewCns :: !(BlockStartStackConstraints arch)
-- ^ New constraints being joined.
, jctxNextCnsRef :: !(STRef s (BlockStartStackConstraints arch))
-- ^ Reference maintaining current bounds.
, jctxClassMapRef :: !(STRef s (JoinClassMap (ArchReg arch)))
-- ^ The map from (oldClassRep, newClassRep) pairs to the
-- next class map.
, jctxClassCountRef :: !(STRef s Int)
-- ^ Reference that stores numbers of classes seen in
-- old reference so we can see if new number of classes
-- is different.
}
-- | Join locations
joinNewLoc :: forall s arch tp
. (MemWidth (ArchAddrWidth arch), OrdF (ArchReg arch))
=> BlockStartStackConstraints arch
-> BlockStartStackConstraints arch
-> STRef s (BlockStartStackConstraints arch)
-- ^ The current index bounds.
-> STRef s (JoinClassMap (ArchReg arch))
-- ^ The map from (oldClassRep, newClassRep) pairs to the
-- result class rep.
-> STRef s Int
-- ^ Stores the number of equivalence classes we have seen in
-- in the old class
-- Used so determining if any classes are changed.
-- | @setNextConstraint ctx loc cns@ asserts that @loc@ satisfies the
-- constraint @cns@ in the next constraint ref of @ctx@.
setNextConstraint :: OrdF (ArchReg arch)
=> JoinContext s arch
-> BoundLoc (ArchReg arch) tp
-> StackEqConstraint (ArchReg arch) tp
-> Changed s ()
setNextConstraint ctx thisLoc eqCns =
changedST $ modifySTRef' (jctxNextCnsRef ctx) $ \cns ->
BSSC (nonOverlapLocInsert thisLoc eqCns (unBSSC cns))
type LocConPair r tp = (BoundLoc r tp, Maybe (StackOffConstraint r tp))
-- | @locAreEqual cns x y@ return true if @cns |= x = y@.
locAreEqual :: ( MemWidth (ArchAddrWidth arch)
, OrdF (ArchReg arch)
)
=> BlockStartStackConstraints arch
-> BoundLoc (ArchReg arch) tp
-> BoundLoc (ArchReg arch) tp
-> Bool
locAreEqual cns x y =
let (xr, _) = blockStartLocRepAndCns cns x
(yr, _) = blockStartLocRepAndCns cns y
in xr == yr
joinNextLoc :: forall s arch tp
. ( MemWidth (ArchAddrWidth arch)
, OrdF (ArchReg arch)
, HasRepr (ArchReg arch) TypeRepr
)
=> JoinContext s arch
-> BoundLoc (ArchReg arch) tp
-- ^ Location in join that we have not yet visited.
-- ^ Location to use for next representative if we need
-- need to make old.
-> LocConPair (ArchReg arch) tp
-> Changed s (BoundLoc (ArchReg arch) tp)
joinNextLoc ctx thisLoc (oldRep, oldPred) = do
-- Get representative in new lcoaton.
let (newRep, newPred) = blockStartLocRepAndCns (jctxNewCns ctx) thisLoc
m <- changedST $ readSTRef (jctxClassMapRef ctx)
-- Make pair containing old and new rep so we can lookup if we
-- already have visited this class.
let pair = JoinClassPair oldRep newRep
case MapF.lookup pair m of
-- If we have visited then we just assert @thisLoc@ is equal to
-- the previously added rep.
Just resRep -> do
-- Assert r is equal to resRep
setNextConstraint ctx thisLoc (EqualLoc resRep)
return resRep
-- If we have not visited these reps, then we need to join the
-- constraint
Nothing -> do
changedST $ writeSTRef (jctxClassMapRef ctx) $! MapF.insert pair thisLoc m
case (oldPred, newPred) of
(Nothing, _) ->
pure ()
(Just (StackOffCns x), Just (StackOffCns y))
| x == y -> do
setNextConstraint ctx thisLoc (IsStackOff x)
-- In old, thisLoc == uext xl w
-- In new, thisLoc == uext yl w
-- We see if xl and yl have same rep in old loc.
-- If so, then we
(Just (UExtCns xl w), Just (UExtCns yl _))
| Just Refl <- testEquality (typeWidth xl) (typeWidth yl)
, locAreEqual (jctxOldCns ctx) xl yl
, locAreEqual (jctxNewCns ctx) xl yl -> do
let xlRep = blockStartLocRepAndCns (jctxOldCns ctx) xl
nextSubRep <- joinNextLoc ctx xl xlRep
setNextConstraint ctx thisLoc (IsUExt nextSubRep w)
(Just _, _) -> do
markChanged True
pure thisLoc
-- | @joinOldLoc ctx l eq@ visits a location in a old map and ensures
-- it is bound.
joinOldLoc :: forall s arch tp
. ( MemWidth (ArchAddrWidth arch)
, OrdF (ArchReg arch)
, HasRepr (ArchReg arch) TypeRepr
)
=> JoinContext s arch
-> BoundLoc (ArchReg arch) tp
-- ^ Location that appears in old constraints.
-> StackEqConstraint (ArchReg arch) tp
-- ^ Constraint on location in original list.
-> Changed s ()
joinNewLoc old new cnsRef procRef cntr thisLoc oldCns = do
(oldRep, oldPred) <- changedST $
joinOldLoc ctx thisLoc oldCns = do
oldRepPredPair <- changedST $
case oldCns of
EqualLoc oldLoc ->
pure (blockStartLocRepAndCns old oldLoc)
pure (blockStartLocRepAndCns (jctxOldCns ctx) oldLoc)
IsStackOff o -> do
-- Increment number of equivalence classes when we see an old
-- representative.
modifySTRef' cntr (+1)
modifySTRef' (jctxClassCountRef ctx) (+1)
-- Return this loc
pure (thisLoc, Just (StackOffCns o))
let (newRep, newPred) = blockStartLocRepAndCns new thisLoc
m <- changedST $ readSTRef procRef
-- See if we have already added a representative for this class.
let pair = JoinClassPair oldRep newRep
case MapF.lookup pair m of
Nothing -> do
mp <- joinStackEqConstraint oldPred newPred
changedST $ do
writeSTRef procRef $! MapF.insert pair thisLoc m
case mp of
Nothing -> pure ()
Just (StackOffCns o) ->
modifySTRef' cnsRef $ \cns ->
BSSC (nonOverlapLocInsert thisLoc (IsStackOff o) (unBSSC cns))
Just resRep ->
-- Assert r is equal to resRep
changedST $
modifySTRef' cnsRef $ \cns ->
BSSC (nonOverlapLocInsert thisLoc (EqualLoc resRep) (unBSSC cns))
IsUExt oldSubLoc w -> do
-- Increment number of equivalence classes when we see an old
-- representative.
modifySTRef' (jctxClassCountRef ctx) (+1)
-- Update predicate to point o
let (subRep, _) = blockStartLocRepAndCns (jctxOldCns ctx) oldSubLoc
pure (thisLoc, Just (UExtCns subRep w))
_ <- joinNextLoc ctx thisLoc oldRepPredPair
pure ()
-- | Return a jump bounds that implies both input bounds, or `Nothing`
-- if every fact inx the old bounds is implied by the new bounds.
joinBlockStartStackConstraints
:: forall arch s
. (MemWidth (ArchAddrWidth arch), OrdF (ArchReg arch))
. (MemWidth (ArchAddrWidth arch)
, OrdF (ArchReg arch)
, HasRepr (ArchReg arch) TypeRepr
)
=> BlockStartStackConstraints arch
-> BlockStartStackConstraints arch
-> Changed s (BlockStartStackConstraints arch, JoinClassMap (ArchReg arch))
@ -603,7 +736,13 @@ joinBlockStartStackConstraints old new = do
-- combined bounds.
procRef <- changedST $ newSTRef MapF.empty
cntr <- changedST $ newSTRef 0
locMapTraverseWithKey_ (joinNewLoc old new cnsRef procRef cntr) (unBSSC old)
let ctx = JoinContext { jctxOldCns = old
, jctxNewCns = new
, jctxNextCnsRef = cnsRef
, jctxClassMapRef = procRef
, jctxClassCountRef = cntr
}
locMapTraverseWithKey_ (joinOldLoc ctx) (unBSSC old)
-- Check number of equivalence classes in result and original
-- The count should not have decreased, but may increase if two elements
-- are no longer equal, and we need to check this.
@ -644,6 +783,14 @@ data StackExpr arch ids tp where
-- | Denotes a constant
CExpr :: !(CValue arch tp) -> StackExpr arch ids tp
-- | This is a pure function applied to other index expressions that
-- may be worth interpreting (but could be treated as an uninterp
-- assign expr also.
UExtExpr :: (1 <= u, u+1 <= w)
=> StackExpr arch ids (BVType u)
-> NatRepr w
-> StackExpr arch ids (BVType w)
-- | This is a pure function applied to other index expressions that
-- may be worth interpreting (but could be treated as an uninterp
-- assign expr also.
@ -651,6 +798,14 @@ data StackExpr arch ids tp where
-> !(App (StackExpr arch ids) tp)
-> StackExpr arch ids tp
instance ShowF (ArchReg arch) => Show (StackExpr arch ids tp) where
show (ClassRepExpr l) = "(ClassRepExpr " <> show l <> ")"
show (UninterpAssignExpr a _r) = "(UninterpAssignExpr " <> show a <> " _)"
show (StackOffsetExpr o) = "(StackOffsetExpr " <> show o <> ")"
show (CExpr c) = "(CExpr " <> show c <> ")"
show (UExtExpr u w) = "(UExtExpr " <> show u <> " " <> show w <> ")"
show (AppExpr a _r) = "(AppExpr " <> show a <> " _)"
instance TestEquality (ArchReg arch) => TestEquality (StackExpr arch ids) where
testEquality (ClassRepExpr x) (ClassRepExpr y) =
testEquality x y
@ -663,6 +818,10 @@ instance TestEquality (ArchReg arch) => TestEquality (StackExpr arch ids) where
Nothing
testEquality (CExpr x) (CExpr y) =
testEquality x y
testEquality (UExtExpr x xw) (UExtExpr y yw) = do
Refl <- testEquality xw yw
Refl <- testEquality x y
pure Refl
testEquality (AppExpr xn _xa) (AppExpr yn _ya) =
testEquality xn yn
testEquality _ _ = Nothing
@ -684,6 +843,12 @@ instance OrdF (ArchReg arch) => OrdF (StackExpr arch ids) where
compareF CExpr{} _ = LTF
compareF _ CExpr{} = GTF
compareF (UExtExpr x xw) (UExtExpr y yw) =
joinOrderingF (compareF xw yw) $
joinOrderingF (compareF x y) $ EQF
compareF UExtExpr{} _ = LTF
compareF _ UExtExpr{} = GTF
compareF (AppExpr xn _xa) (AppExpr yn _ya) = compareF xn yn
instance TestEquality (ArchReg arch) => Eq (StackExpr arch ids tp) where
@ -701,6 +866,9 @@ instance ( HasRepr (ArchReg arch) TypeRepr
UninterpAssignExpr _ rhs -> typeRepr rhs
StackOffsetExpr _ -> addrTypeRepr
CExpr x -> typeRepr x
UExtExpr (_ :: StackExpr arch ids (BVType u)) (w :: NatRepr w) ->
case leqTrans (leqAdd (LeqProof @1 @u) (Proxy :: Proxy 1)) (LeqProof @(u+1) @w) of
LeqProof -> BVTypeRepr w
AppExpr _ a -> typeRepr a
instance ShowF (ArchReg arch) => Pretty (StackExpr arch id tp) where
@ -708,9 +876,13 @@ instance ShowF (ArchReg arch) => Pretty (StackExpr arch id tp) where
case e of
ClassRepExpr l -> pretty l
UninterpAssignExpr n _ -> parens (text "uninterp" <+> ppAssignId n)
StackOffsetExpr o -> parens (text "+ stack_off" <+> pretty o)
StackOffsetExpr o
| memIntValue o > 0 -> parens (text "+ stack_off" <+> pretty o)
| memIntValue o < 0 -> parens (text "- stack_off" <+> pretty (negate (toInteger (memIntValue o))))
| otherwise -> text "stack_off"
CExpr v -> pretty v
AppExpr n _ -> parens (text "app" <+> ppAssignId n)
UExtExpr l w -> parens (text "uext " <> pretty l <+> text (show w))
AppExpr n _ -> ppAssignId n
instance ShowF (ArchReg arch) => PrettyF (StackExpr arch id) where
prettyF = pretty
@ -728,6 +900,7 @@ blockStartLocExpr cns loc =
Nothing -> ClassRepExpr loc
Just (IsStackOff o) -> StackOffsetExpr o
Just (EqualLoc loc') -> blockStartLocExpr cns loc'
Just (IsUExt loc' w) -> UExtExpr (blockStartLocExpr cns loc') w
------------------------------------------------------------------------
-- BlockIntraStackConstraints
@ -743,6 +916,27 @@ data BlockIntraStackConstraints arch ids
-- ^ Maps processed assignments to index expressions.
}
instance ShowF (ArchReg arch) => Pretty (BlockIntraStackConstraints arch ids) where
pretty cns =
let ppStk :: MemInt (ArchAddrWidth arch) -> MemRepr tp -> StackExpr arch ids tp -> Doc
ppStk o r v = text (show o) <> text ", " <> pretty r <> text " := " <> pretty v
sm :: Doc
sm = memMapFoldrWithKey (\o r v d -> ppStk o r v <$$> d) empty (stackExprMap cns)
ppAssign :: AssignId ids tp -> StackExpr arch ids tp -> Doc -> Doc
ppAssign a (AppExpr u app) d =
case testEquality a u of
Nothing -> ppAssignId a <> text " := " <> ppAssignId u <$$> d
Just Refl -> ppAssignId a <> text " := " <> ppApp pretty app <$$> d
ppAssign a e d = ppAssignId a <> text " := " <> pretty e <$$> d
am :: Doc
am = MapF.foldrWithKey ppAssign empty (assignExprMap cns)
in vcat [ text "stackExprMap:"
, indent 2 sm
, text "assignExprMap:"
, indent 2 am
]
-- | Create index bounds from initial index bounds.
mkBlockIntraStackConstraints :: forall arch ids
. (MemWidth (ArchAddrWidth arch), OrdF (ArchReg arch))
@ -762,7 +956,7 @@ mkBlockIntraStackConstraints cns =
-- | Return the value of the index expression given the bounds.
intraStackValueExpr :: forall arch ids tp
. (MemWidth (ArchAddrWidth arch), OrdF (ArchReg arch))
=> BlockIntraStackConstraints arch ids
=> BlockIntraStackConstraints arch ids
-> Value arch ids tp
-> StackExpr arch ids tp
intraStackValueExpr cns val =
@ -787,6 +981,8 @@ intraStackRhsExpr :: forall arch ids tp
-> StackExpr arch ids tp
intraStackRhsExpr cns aid arhs =
case arhs of
EvalApp (UExt x w) ->
UExtExpr (intraStackValueExpr cns x) w
EvalApp app -> do
let stackFn v =
case intraStackValueExpr cns v of
@ -815,7 +1011,7 @@ intraStackSetAssignId aid expr cns =
-- | Discard information about memory due to an operation that may
-- affect arbitrary memory.
discardMemInfo :: BlockIntraStackConstraints arch ids
-> BlockIntraStackConstraints arch ids
-> BlockIntraStackConstraints arch ids
discardMemInfo cns =
cns { stackExprMap = emptyMemMap }

View File

@ -55,12 +55,15 @@ type family ArchReg (arch :: Kind.Type) = (reg :: Type -> Kind.Type) | reg -> ar
--
-- The functions associated with architecture-function depend on the
-- contents of memory and implicit components of the processor state,
-- but they should not affect any of the architecture registers in
-- @ArchReg arch@. They may only depend on the value stored in a
-- general purpose register if it is passed as a value that can be
-- visited when folding over values. In particular,
-- but they should not affect any of the architecture
-- registers in @ArchReg arch@.
--
-- One Furthermore, they can only return a value
-- Architecture specific functions are required to implement
-- `FoldableFC`, and the evaluation of an architecture specific
-- function may only depend on the value stored in a general purpose
-- register if it is given through the @fn@ parameter and provided
-- when folding over values. This is required for accurate def-use
-- analysis of general purpose registers.
type family ArchFn (arch :: Kind.Type) = (fn :: (Type -> Kind.Type) -> Type -> Kind.Type) | fn -> arch
-- | A type family for defining architecture-specific statements.

View File

@ -255,7 +255,11 @@ rewriteApp app = do
pure y
else
pure (BoolValue False)
AndApp x y@BoolValue{} -> rewriteApp (AndApp y x)
AndApp x (BoolValue yc) ->
if yc then
pure x
else
pure (BoolValue False)
-- x < y && x <= y = x < y
AndApp (valueAsApp -> Just (BVUnsignedLe x y ))
v@(valueAsApp -> Just (BVUnsignedLt x' y'))

View File

@ -99,6 +99,8 @@ import System.IO
import Text.PrettyPrint.ANSI.Leijen (pretty)
import Text.Printf (printf)
#define USE_REWRITER
import Data.Macaw.AbsDomain.AbsState
import qualified Data.Macaw.AbsDomain.JumpBounds as Jmp
import Data.Macaw.AbsDomain.Refine
@ -106,14 +108,15 @@ import qualified Data.Macaw.AbsDomain.StridedInterval as SI
import Data.Macaw.Architecture.Info
import Data.Macaw.CFG
import Data.Macaw.CFG.DemandSet
#ifdef USE_REWRITER
import Data.Macaw.CFG.Rewriter
#endif
import Data.Macaw.DebugLogging
import Data.Macaw.Discovery.AbsEval
import Data.Macaw.Discovery.State as State
import qualified Data.Macaw.Memory.Permissions as Perm
import Data.Macaw.Types
#define USE_REWRITER 1
------------------------------------------------------------------------
-- Utilities
@ -799,7 +802,10 @@ matchRelativeJumpTable = classifierName "Relative jump table" $ do
-- ip = jmptbl + jmptbl[index]
-- where jmptbl is a pointer to the lookup table.
Just unalignedIP <- pure $ fromIPAligned ip
Just (tgtBase, tgtOffset) <- pure $ valueAsMemOffset mem aps unalignedIP
(tgtBase, tgtOffset) <-
case valueAsMemOffset mem aps unalignedIP of
Just p -> pure p
Nothing -> fail $ "Unaligned IP not a mem offset: " <> show unalignedIP
SomeExt shortOffset ext <- pure $ matchExtension tgtOffset
(arrayRead, idx) <- lift $ matchBoundedMemArray mem aps jmpBounds shortOffset
unless (isReadOnlyBoundedMemArray arrayRead) $ do
@ -1528,7 +1534,6 @@ addBlock src finfo pr = do
s <- use curFunCtx
let ainfo = archInfo s
let initRegs = initialBlockRegs ainfo src pr
let mem = memory s
nonceGen <- gets funNonceGen
prev_block_map <- use $ curFunBlocks
@ -1541,6 +1546,7 @@ addBlock src finfo pr = do
-- Rewrite returned blocks to simplify expressions
#ifdef USE_REWRITER
(_,b) <- do
let mem = memory s
let archStmt = rewriteArchStmt ainfo
let secAddrMap = memSectionIndexMap mem
let rw = \_ -> pure

View File

@ -325,7 +325,6 @@ data X86PrimFn f tp where
-- > return (true, eax, edx)
-- > else
-- > return (false, trunc 32 temp64, trunc 32 (temp64 >> 32))
--
CMPXCHG8B :: !(f (BVType 64))
-- Address to read
-> !(f (BVType 32))

View File

@ -977,6 +977,11 @@ x .=. y
| ValueExpr (BoolValue True) <- y = x
| ValueExpr (BoolValue False) <- y = boolNot x
-- Rewrite (u-v) .=. 0 to @u .=. v@.
| ValueExpr (BVValue _ 0) <- y
, Just (BVSub _ u v) <- asApp x =
u .=. v
-- General case
| otherwise = app $ Eq x y
@ -1173,6 +1178,15 @@ bvUlt :: (1 <= n) => Expr ids (BVType n) -> Expr ids (BVType n) -> Expr ids Bool
bvUlt x y
| Just xv <- asUnsignedBVLit x, Just yv <- asUnsignedBVLit y = boolValue (xv < yv)
| x == y = false
-- Simplify @xlit < uext y w@ to @xlit < y@ or @False@ depending on literal constant.
| Just xc <- asUnsignedBVLit x
, Just (UExt ysub _) <- asApp y
, wsub <- typeWidth ysub =
-- If constant is greater than max value then this is just false.
if xc >= maxUnsigned wsub then
ValueExpr (BoolValue False)
else
app $ BVUnsignedLt (ValueExpr (BVValue wsub xc)) ysub
| otherwise = app $ BVUnsignedLt x y
-- | Unsigned less than or equal.
@ -1364,6 +1378,10 @@ boolNot x
| Just xv <- asBoolLit x = boolValue (not xv)
-- not (not p) = p
| Just (NotApp y) <- asApp x = y
| Just (BVUnsignedLe u v) <- asApp x = AppExpr (BVUnsignedLt v u)
| Just (BVUnsignedLt u v) <- asApp x = AppExpr (BVUnsignedLe v u)
| Just (BVSignedLe u v) <- asApp x = AppExpr (BVSignedLt v u)
| Just (BVSignedLt u v) <- asApp x = AppExpr (BVSignedLe v u)
| otherwise = app $ NotApp x
(.&&.) :: Expr ids BoolType -> Expr ids BoolType -> Expr ids BoolType
@ -1381,8 +1399,8 @@ x .&&. y
, Just (Eq y1 y2) <- asApp yc
, BVTypeRepr w <- typeRepr y1
, Just Refl <- testEquality (typeWidth x1) w
, ((x1,x2) == (y1,y2) || (x1,x2) == (y2,y1)) =
bvUlt x1 x2
, (x1,x2) == (y1,y2) || (x1,x2) == (y2,y1) =
bvUlt x1 x2
-- x1 ~= x2 & x1 <= x2 => x1 < x2
| Just (BVUnsignedLe y1 y2) <- asApp y
@ -1390,8 +1408,8 @@ x .&&. y
, Just (Eq x1 x2) <- asApp xc
, BVTypeRepr w <- typeRepr x1
, Just Refl <- testEquality w (typeWidth y1)
, ((x1,x2) == (y1,y2) || (x1,x2) == (y2,y1)) =
bvUlt y1 y2
, (x1,x2) == (y1,y2) || (x1,x2) == (y2,y1) =
bvUlt y1 y2
-- Default case
| otherwise = app $ AndApp x y

View File

@ -1,5 +1,6 @@
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE DataKinds #-}
module ElfX64Linux (
@ -82,7 +83,7 @@ toAddr segOff = do
-- | Run a test over a given expected result filename and the ELF file
-- associated with it
testDiscovery :: FilePath -> E.Elf 64 -> IO ()
testDiscovery :: FilePath -> E.ElfHeaderInfo 64 -> IO ()
testDiscovery expectedFilename elf = do
let opt = MM.defaultLoadOptions
(warn, mem, mentry, syms) <-
@ -119,9 +120,12 @@ testDiscovery expectedFilename elf = do
unless (S.member addr ignoredBlocks) $ do
let term = MD.pblockTermStmt pb
case term of
MD.ClassifyFailure _ rsns ->
MD.ClassifyFailure _ rsns -> do
-- Print a reason with subsequent line sindented more.
let ppRsn [] = []
ppRsn (h:r) = (" " <> h) : fmap (\s -> " " <> s) r
T.assertFailure $ "Unclassified block at " ++ show (MD.pblockAddr pb) ++ "\n"
++ unlines ((\s -> " " ++ s) <$> rsns)
++ unlines (concatMap (ppRsn . lines) rsns)
MD.ParsedTranslateError _ ->
T.assertFailure $ "Translate error at " ++ show (MD.pblockAddr pb) ++ " " ++ show term
_ ->
@ -144,16 +148,16 @@ testDiscovery expectedFilename elf = do
expectedBlockStarts
actualBlockStarts
withELF :: FilePath -> (E.Elf 64 -> IO ()) -> IO ()
withELF :: FilePath -> (E.ElfHeaderInfo 64 -> IO ()) -> IO ()
withELF fp k = do
bytes <- B.readFile fp
case E.parseElf bytes of
E.ElfHeaderError off msg ->
case E.decodeElfHeaderInfo bytes of
Left (off,msg) ->
error ("Error parsing ELF header at offset " ++ show off ++ ": " ++ msg)
E.Elf32Res [] _e32 -> error "ELF32 is unsupported in the test suite"
E.Elf64Res [] e64 -> k e64
E.Elf32Res errs _ -> error ("Errors while parsing ELF file: " ++ show errs)
E.Elf64Res errs _ -> error ("Errors while parsing ELF file: " ++ show errs)
Right (E.SomeElf e) ->
case E.headerClass (E.header e) of
E.ELFCLASS32 -> error "ELF32 is unsupported in the test suite"
E.ELFCLASS64 -> k e
data ElfException = MemoryLoadError String
deriving (Typeable, Show)