You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Negative-sign check runs before the zero-start-pointer guard: 0x2D at memory address 0 bypasses ZeroStringStartPointer #42
src/lib/parse/LibParseDecimal.sol:115-118 vs :30-32.
Intent oracle
ZeroStringStartPointer exists to reject start == 0 outright (the unchecked cursor >= start loop bound degenerates when start is 0).
Violated property
unsafeDecimalStringToSignedInt(0, end) calls LibParseChar.isMask(0, end, CMASK_NEGATIVE_SIGN) BEFORE any zero check. If the byte at memory address 0 (scratch space) happens to be 0x2D ('-'), isNeg is 1, the inner parse is called with start = 1, and the revert designed for zero start pointers never fires — the parse proceeds reading from address 1.
Verified repro
Trace: signed(0, end>0) → isMask reads byte(0, mload(0)) → scratch-dependent. The existing test testExternalTestZeroStringStartPointer (signed variant) passes only because scratch at address 0 is typically not 0x2D in that call context; nothing pins the guard for the 0x2D case.
Triage framing
Reachability requires a caller both passing start=0 and having 0x2D in scratch — out-of-contract usage twice over. Surfaced because the guard's purpose is exactly to make out-of-contract usage loud, and it is bypassable by memory state the caller does not control. Cheap fix: hoist a start == 0 check (or the sign check) so the guard is unconditional.
Activity