Skip to content

Commit

Permalink
Merge branch 'master' of github.com:GaloisInc/pate
Browse files Browse the repository at this point in the history
  • Loading branch information
jim-carciofini committed Feb 12, 2024
2 parents c6b3dd8 + cf9c222 commit c0b8ce6
Show file tree
Hide file tree
Showing 13 changed files with 181 additions and 26 deletions.
11 changes: 2 additions & 9 deletions src/Pate/CLI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ mkRunConfig archLoader opts mtt = let
, PC.cfgTargetEquivRegs = targetEquivRegs opts
, PC.cfgRescopingFailureMode = rerrMode opts
, PC.cfgScriptPath = scriptPath opts
, PC.cfgTraceTree = fromMaybe noTraceTree mtt
}
cfg = PL.RunConfig
{ PL.archLoader = archLoader
Expand All @@ -105,15 +106,7 @@ mkRunConfig archLoader opts mtt = let
, PL.useDwarfHints = not $ noDwarfHints opts
, PL.elfLoaderConfig = PLE.defaultElfLoaderConfig { PLE.ignoreSegments = ignoreSegments opts }
}

in case PC.cfgScriptPath (PL.verificationCfg cfg) of
Just fp -> PSc.readScript fp >>= \case
Left err -> return $ Left (show err)
Right scr -> do
let tt = fromMaybe noTraceTree mtt
tt' <- PSc.attachToTraceTree scr tt
return $ Right $ PL.setTraceTree tt' cfg
Nothing -> return $ Right $ PL.setTraceTree (fromMaybe noTraceTree mtt) cfg
in PL.parseAndAttachScript cfg

data CLIOptions = CLIOptions
{ originalBinary :: FilePath
Expand Down
15 changes: 14 additions & 1 deletion src/Pate/Loader.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ module Pate.Loader
, runSelfEquivConfig
, runEquivConfig
, RunConfig(..)
, setTraceTree
, Logger(..)
, parseAndAttachScript
)
where

Expand Down Expand Up @@ -58,6 +58,19 @@ data RunConfig =
, elfLoaderConfig :: ElfLoaderConfig
}

parseAndAttachScript ::
RunConfig ->
IO (Either String RunConfig)
parseAndAttachScript cfg = case PC.cfgScriptPath (verificationCfg cfg) of
Just fp -> PS.readScript fp >>= \case
Left err -> return $ Left (show err)
Right scr -> do
let tt = PC.cfgTraceTree $ verificationCfg cfg
tt' <- PS.attachToTraceTree scr tt
return $ Right $ setTraceTree tt' cfg
Nothing -> return $ Right cfg


setTraceTree :: SomeTraceTree PA.ValidRepr -> RunConfig -> RunConfig
setTraceTree traceTree rcfg = rcfg { verificationCfg = (verificationCfg rcfg){ PC.cfgTraceTree = traceTree }}

Expand Down
4 changes: 2 additions & 2 deletions src/Pate/Script.hs
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ matchAnyAct = NodeFinalAction $ \_ -> return True

parseIdentQuery :: Parser NodeIdentQuery
parseIdentQuery =
(do
i <- int
P.try (do
i <- int
_ <- ":"
s <- P.many (P.notFollowedBy (P.newline) >> L.charLiteral)
case s of
Expand Down
8 changes: 5 additions & 3 deletions src/Pate/TraceTree.hs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ import Control.Monad (void, unless, forM) -- GHC 9.6
import Control.Applicative

import qualified Prettyprinter as PP
import qualified Prettyprinter.Render.String as PP

import qualified Data.Aeson as JSON
import qualified Compat.Aeson as JSON
Expand Down Expand Up @@ -377,13 +378,14 @@ resolveQuery (NodeQuery (q_outer:qs_outer) fin_outer) t_outer =
-- we need to reverse it here so the indices match up
fmap catMaybes $ forM (zip [0..] (reverse nodes')) $ \(i,(SomeTraceNode (nm :: SymbolRepr nm) lbl v, t)) -> do
Just pp <- return $ getNodePrinter @k @nm [Simplified]
let as_string = PP.renderString $ PP.layoutPretty (PP.defaultLayoutOptions { PP.layoutPageWidth = PP.Unbounded }) (pp lbl v)
let matched = case q of
QueryInt i' -> i == i'
QueryString s -> isPrefixOf s (show (pp lbl v))
QueryStringInt i' s -> i == i' && isPrefixOf s (show (pp lbl v))
QueryString s -> isPrefixOf s as_string
QueryStringInt i' s -> i == i' && isPrefixOf s as_string
QueryAny -> True
case matched of
True -> return $ Just $ (QueryStringInt i (show (pp lbl v)), SomeTraceNode nm lbl v,t)
True -> return $ Just $ (QueryStringInt i as_string, SomeTraceNode nm lbl v,t)
False -> return Nothing

data InteractionMode =
Expand Down
14 changes: 10 additions & 4 deletions src/Pate/Verification/PairGraph.hs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ module Pate.Verification.PairGraph

import Prettyprinter

import Control.Monad (foldM)
import Control.Monad (foldM, guard)
import Control.Monad.IO.Class
import qualified Control.Lens as L
import Control.Lens ( (&), (.~), (^.), (%~) )
Expand Down Expand Up @@ -147,7 +147,7 @@ import qualified Pate.Verification.Domain as PVD
import qualified Pate.SimState as PS


import Pate.Verification.PairGraph.Node ( GraphNode(..), NodeEntry, NodeReturn, pattern GraphNodeEntry, pattern GraphNodeReturn, rootEntry, nodeBlocks, rootReturn, nodeFuns, graphNodeBlocks, getDivergePoint )
import Pate.Verification.PairGraph.Node ( GraphNode(..), NodeEntry, NodeReturn, pattern GraphNodeEntry, pattern GraphNodeReturn, rootEntry, nodeBlocks, rootReturn, nodeFuns, graphNodeBlocks, getDivergePoint, divergePoint, mkNodeEntry, mkNodeReturn, nodeContext )
import Pate.Verification.StrongestPosts.CounterExample ( TotalityCounterexample(..), ObservableCounterexample(..) )

import qualified Pate.Verification.AbstractDomain as PAD
Expand Down Expand Up @@ -972,17 +972,23 @@ combineNodes node1 node2 = do
(nodeO, nodeP) <- case PPa.get PBi.OriginalRepr (graphNodeBlocks node1) of
Just{} -> return (node1, node2)
Nothing -> return (node2, node1)
-- it only makes sense to combine nodes that share a divergence point,
-- where that divergence point will be used as the calling context for the
-- merged point
GraphNode divergeO <- divergePoint $ nodeContext nodeO
GraphNode divergeP <- divergePoint $ nodeContext nodeP
guard $ divergeO == divergeP
case (nodeO, nodeP) of
(GraphNode nodeO', GraphNode nodeP') -> do
blocksO <- PPa.get PBi.OriginalRepr (nodeBlocks nodeO')
blocksP <- PPa.get PBi.PatchedRepr (nodeBlocks nodeP')
-- FIXME: retain calling context?
return $ GraphNode $ rootEntry (PPa.PatchPair blocksO blocksP)
return $ GraphNode $ mkNodeEntry divergeO (PPa.PatchPair blocksO blocksP)
(ReturnNode nodeO', ReturnNode nodeP') -> do
fnsO <- PPa.get PBi.OriginalRepr (nodeFuns nodeO')
fnsP <- PPa.get PBi.PatchedRepr (nodeFuns nodeP')
-- FIXME: retain calling context?
return $ ReturnNode $ rootReturn (PPa.PatchPair fnsO fnsP)
return $ ReturnNode $ mkNodeReturn divergeO (PPa.PatchPair fnsO fnsP)
_ -> Nothing

singleNodeRepr :: GraphNode arch -> Maybe (Some (PBi.WhichBinaryRepr))
Expand Down
5 changes: 5 additions & 0 deletions src/Pate/Verification/PairGraph/Node.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ module Pate.Verification.PairGraph.Node (
, CallingContext
, pattern GraphNodeEntry
, pattern GraphNodeReturn
, nodeContext
, divergePoint
, graphNodeBlocks
, mkNodeEntry
, mkNodeEntry'
Expand Down Expand Up @@ -87,6 +89,9 @@ graphNodeBlocks :: GraphNode arch -> PB.BlockPair arch
graphNodeBlocks (GraphNode ne) = nodeBlocks ne
graphNodeBlocks (ReturnNode ret) = TF.fmapF PB.functionEntryToConcreteBlock (nodeFuns ret)

nodeContext :: GraphNode arch -> CallingContext arch
nodeContext (GraphNode nd) = graphNodeContext nd
nodeContext (ReturnNode ret) = returnNodeContext ret

pattern GraphNodeEntry :: PB.BlockPair arch -> GraphNode arch
pattern GraphNodeEntry blks <- (GraphNode (NodeEntry _ blks))
Expand Down
3 changes: 1 addition & 2 deletions tests/AArch32TestMain.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ main = do
{ testArchName = "aarch32"
, testArchLoader = AArch32.archLoader
, testExpectEquivalenceFailure =
[ "stack-struct", "unequal/stack-struct", "max-signed",
[ "stack-struct", "unequal/stack-struct", "max-signed"
-- missing interactive test support
"desync-defer", "desync-simple"
]
, testExpectSelfEquivalenceFailure = []
-- TODO: we should define a section name here and read its address
Expand Down
20 changes: 15 additions & 5 deletions tests/TestBase.hs
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ doTest ::
doTest mwb cfg sv fp = do
infoCfgExists <- doesFileExist (fp <.> "toml")
argFileExists <- doesFileExist (fp <.> "args")
scriptFileExists <- doesFileExist (fp <.> "pate")

(logsRef :: IOR.IORef [String]) <- IOR.newIORef []

let
addLogMsg :: String -> IO ()
addLogMsg msg = IOR.atomicModifyIORef' logsRef $ \logs -> (msg : logs, ())
Expand All @@ -158,9 +158,14 @@ doTest mwb cfg sv fp = do
let (msg, _exit) = OA.renderFailure failure progn
failTest ("Input: \n" ++ (show optsList) ++ "\n" ++ msg)
_ -> failTest "unexpected parser result"
False -> let
infoPath = if infoCfgExists then Just $ fp <.> "toml" else Nothing
in return $ ("./", PL.RunConfig
False -> do
let infoPath = if infoCfgExists then Just $ fp <.> "toml" else Nothing
-- only include the script for the equivalence proof, for self-equivalence we
-- assume no script
-- NB: this is not necessarily true if code discovery requires input, but we don't have
-- any tests that require this at the moment
let scriptPath = if scriptFileExists && not (isJust mwb) then Just (fp <.> "pate") else Nothing
mrcfg <- PL.parseAndAttachScript $ PL.RunConfig
{ PL.patchInfoPath = infoPath
, PL.patchData = defaultPatchData cfg
, PL.origPaths = PLE.simplePaths (fp <.> "original" <.> "exe")
Expand All @@ -173,6 +178,7 @@ doTest mwb cfg sv fp = do
, PC.cfgIgnoreUnnamedFunctions = False
, PC.cfgIgnoreDivergedControlFlow = False
, PC.cfgStackScopeAssume = False
, PC.cfgScriptPath = scriptPath
}
, PL.logger = \(PA.SomeValidArch{}) -> do
let
Expand All @@ -196,7 +202,11 @@ doTest mwb cfg sv fp = do
, PL.archLoader = testArchLoader cfg
, PL.useDwarfHints = False
, PL.elfLoaderConfig = PLE.defaultElfLoaderConfig
})
}
case mrcfg of
Left err -> failTest err
Right rcfg -> return ("./", rcfg)

result <- withCurrentDirectory dir $ case mwb of
Just wb -> PL.runSelfEquivConfig rcfg wb
Nothing -> PL.runEquivConfig rcfg
Expand Down
43 changes: 43 additions & 0 deletions tests/aarch32/desync-defer.pate
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Choose Entry Point
> Function Entry "_start"

Function Entry "f" (0x10158)
...
Call to: "malloc" (0x10104) Returns to: "f" (0x10178) (original) vs. Call to: "write" (0x100d8) Returns to: "f" (0x101a8) (patched)
...
...
> Choose desynchronization points

...
Choose a desynchronization point:
> 0x1016c (original)
> 0x1016c (patched)
// 0x1070 - should be this, but the splitting for desync points is incorrect

0x10170 [ via: "f" (0x10158) <- "_start" (0x10218) (original) vs. "_start" (0x10214) (patched) ]
...
Call to: "malloc"
...
...
> Choose synchronization points

...
Choose a synchronization point:
> 0x10188 (original)
> 0x10178 (patched)

0x101e8 (original) vs. 0x101d8 (patched)
...
Return (original) vs. Call to: "g"
...
...
> Choose synchronization points

...
Choose a synchronization point:
> Return "f" (0x10158)
// applies to both original and patched

Verification Finished
Continue verification?
> Finish and view final result
18 changes: 18 additions & 0 deletions tests/aarch32/desync-simple.pate
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Choose Entry Point
> Function Entry "_start"

Function Entry "f"
...
Call to: "clock"
...
...
> Choose synchronization points

...
Choose a synchronization point:
> 0x8058 (original)
> 0x8054 (patched)

Verification Finished
Continue verification?
> Finish and view final result
43 changes: 43 additions & 0 deletions tests/aarch32/unequal/desync-defer.pate
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Choose Entry Point
> Function Entry "_start"

Function Entry "f" (0x10158)
...
Call to: "malloc" (0x10104) Returns to: "f" (0x10178) (original) vs. Call to: "write" (0x100d8) Returns to: "f" (0x101a8) (patched)
...
...
> Choose desynchronization points

...
Choose a desynchronization point:
> 0x1016c (original)
> 0x1016c (patched)
// 0x1070 - should be this, but the splitting for desync points is incorrect

0x10170 [ via: "f" (0x10158) <- "_start" (0x10218) (original) vs. "_start" (0x10214) (patched) ]
...
Call to: "malloc"
...
...
> Choose synchronization points

...
Choose a synchronization point:
> 0x10188 (original)
> 0x10178 (patched)

0x101e8 (original) vs. 0x101d8 (patched)
...
Return (original) vs. Call to: "g"
...
...
> Choose synchronization points

...
Choose a synchronization point:
> Return "f" (0x10158)
// applies to both original and patched

Verification Finished
Continue verification?
> Finish and view final result
18 changes: 18 additions & 0 deletions tests/aarch32/unequal/desync-simple.pate
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Choose Entry Point
> Function Entry "_start"

Function Entry "f"
...
Call to: "clock"
...
...
> Choose synchronization points

...
Choose a synchronization point:
> 0x8058 (original)
> 0x8054 (patched)

Verification Finished
Continue verification?
> Finish and view final result
5 changes: 5 additions & 0 deletions tests/template.mk
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ malloc-%.exe: ./build/malloc-%.s ./build/link.ld
diff ./dumps/$(basename $@).original.dump ./dumps/$(basename $@).patched.dump || true
diff ./dumps/$(basename $@).original.dump ./dumps/$(basename $@).patched-bad.dump || true

%.test_run: %.original.exe %.patched.exe
../../pate.sh -o $(basename $@).original.exe -p $(basename $@).patched.exe \
`( (test -f $(basename $@).toml && echo "-b $(basename $@).toml") || echo "")` \
`( (test -f $(basename $@).pate && echo "--script $(basename $@).pate") || echo "")`

.PRECIOUS: ./build/%.s ./build/%.i %.exe malloc-%.exe ./unequal/%.original.exe ./unequal/%.patched.exe

clean:
Expand Down

0 comments on commit c0b8ce6

Please sign in to comment.