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:
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