auditor

An aarch64 constant-time memory access auditing tool.
git clone git://git.ppad.tech/auditor.git
Log | Files | Refs | README | LICENSE

commit a5ec87850ea6e1d5c4ab1d34df162966eab9de9a
parent fb15d345eb83db9a270c3f937552e2a1c2ef7ba1
Author: Jared Tobin <jared@jtobin.io>
Date:   Tue, 10 Feb 2026 14:49:05 +0400

fix: parser fixes for secp256k1.s compatibility

- Fix backtracking for post-index addressing: wrap pPostIndex in try
  to allow backtracking when whitespace is consumed before failing on
  non-comma (e.g., semicolon comment after [sp])

- Parse optional shift amount after extend ops: handle [x9, w8, uxtw #3]
  patterns where extend has a shift amount

- Add --parse flag and parseFile for parse-only mode (useful for
  debugging parser issues)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Diffstat:
Mapp/Main.hs | 38+++++++++++++++++++++++++++-----------
Mlib/Audit/AArch64.hs | 12++++++++++++
Mlib/Audit/AArch64/Parser.hs | 18+++++++++++-------
3 files changed, 50 insertions(+), 18 deletions(-)

diff --git a/app/Main.hs b/app/Main.hs @@ -3,7 +3,7 @@ module Main where import Audit.AArch64 (AuditResult(..), Violation(..), ViolationReason(..), - auditFile, auditFileInterProc, regName) + auditFile, auditFileInterProc, parseFile, regName) import Data.Aeson (encode) import qualified Data.ByteString.Lazy.Char8 as BL import Data.Text (Text) @@ -17,6 +17,7 @@ data Options = Options , optJson :: !Bool , optQuiet :: !Bool , optInterProc :: !Bool + , optParseOnly :: !Bool } deriving (Eq, Show) optParser :: Parser Options @@ -42,6 +43,10 @@ optParser = Options <> short 'p' <> help "Enable inter-procedural analysis" ) + <*> switch + ( long "parse" + <> help "Parse only, no analysis" + ) optInfo :: ParserInfo Options optInfo = info (optParser <**> helper) @@ -53,16 +58,27 @@ optInfo = info (optParser <**> helper) main :: IO () main = do opts <- execParser optInfo - let auditor = if optInterProc opts then auditFileInterProc else auditFile - result <- auditor (optInput opts) - case result of - Left err -> do - TIO.putStrLn $ "Error: " <> err - exitFailure - Right ar -> - if optJson opts - then outputJson ar - else outputText opts ar + if optParseOnly opts + then do + result <- parseFile (optInput opts) + case result of + Left err -> do + TIO.putStrLn $ "Error: " <> err + exitFailure + Right n -> do + TIO.putStrLn $ "Parsed " <> T.pack (show n) <> " lines" + exitSuccess + else do + let auditor = if optInterProc opts then auditFileInterProc else auditFile + result <- auditor (optInput opts) + case result of + Left err -> do + TIO.putStrLn $ "Error: " <> err + exitFailure + Right ar -> + if optJson opts + then outputJson ar + else outputText opts ar outputJson :: AuditResult -> IO () outputJson ar = BL.putStrLn (encode (arViolations ar)) diff --git a/lib/Audit/AArch64.hs b/lib/Audit/AArch64.hs @@ -33,6 +33,7 @@ module Audit.AArch64 ( , auditInterProc , auditFile , auditFileInterProc + , parseFile -- * Results , AuditResult(..) @@ -87,3 +88,14 @@ auditFileWith auditor path = do case auditor (T.pack path) src of Left err -> pure (Left (T.pack (show err))) Right result -> pure (Right result) + +-- | Parse an assembly file without analysis. Returns line count on success. +parseFile :: FilePath -> IO (Either Text Int) +parseFile path = do + bs <- BS.readFile path + case decodeUtf8' bs of + Left err -> pure (Left (T.pack (show err))) + Right src -> + case parseAsm src of + Left err -> pure (Left (T.pack (show err))) + Right lns -> pure (Right (length lns)) diff --git a/lib/Audit/AArch64/Parser.hs b/lib/Audit/AArch64/Parser.hs @@ -433,7 +433,7 @@ pBracketAddr = lexeme $ do void (char ']') -- Check for pre-index ! or post-index offset preIdx <- option False (True <$ char '!') - postIdx <- optional pPostIndex + postIdx <- optional (try pPostIndex) case (innerMode, preIdx, postIdx) of (Nothing, False, Nothing) -> pure (BaseImm base 0) (Nothing, False, Just imm) -> pure (PostIndex base imm) @@ -472,12 +472,16 @@ pShiftOrExtend = choice ] pExtend :: Parser Extend -pExtend = lexeme $ choice - [ UXTW <$ string' "uxtw" - , SXTW <$ string' "sxtw" - , UXTX <$ string' "uxtx" - , SXTX <$ string' "sxtx" - ] +pExtend = lexeme $ do + ext <- choice + [ UXTW <$ string' "uxtw" + , SXTW <$ string' "sxtw" + , UXTX <$ string' "uxtx" + , SXTX <$ string' "sxtx" + ] + -- Optionally consume shift amount (e.g., #3) - ignored for taint analysis + void $ optional (sc *> pImm) + pure ext pPostIndex :: Parser Integer pPostIndex = do