Skip to content

Fix UB in parseProof(): do not read uninitialized memory - #209

Open
benjub wants to merge 2 commits into
metamath:masterfrom
benjub:fix-parseproof-uninit
Open

benjub wants to merge 2 commits into
metamath:masterfrom
benjub:fix-parseproof-uninit

Conversation

@benjub

@benjub benjub commented Oct 2, 2026

Copy link
Copy Markdown
Collaborator

Depends on #208; first commit is 083ed66

parseProof() copied the entire array proofString at every proof step with hypotheses, although only steps 0 to step are built. In the first proof the rest is uninitialized memory, so copying them is UB (as reported by MemorySanitizer). Copy only the built steps.

Results are unchanged, since only steps before step are copied back. Verifying /EXPLICIT proofs gets slightly faster.

The case open-msan-parseproof-uninit added in #208 now passes, so this renames it parseproof-uninit and marks it fixed.

Written with help from Claude code.

build-sanitizer.sh also builds ./metamath-msan with MemorySanitizer, which
reports reads of uninitialized memory.  run-cases.sh runs the cases
against it too; an open case is checked only against the binary whose
sanitizer found it.

MemorySanitizer finds a bug that AddressSanitizer misses:
parseProof() copies an uninitialized proofString element.  It is added as
the case open-msan-parseproof-uninit.

run-cases.sh and fuzz.py now recognize MemorySanitizer reports: this build
exits with status 1, as metamath does on an ordinary error, so the status
alone does not show them.  Also fix a bug where fuzz.py could not run a
binary given by a relative path, as in the README's own example, because
each run happens in a temporary directory.
To put the hypotheses of an /EXPLICIT proof back in order, parseProof()
copied the whole proof string at every step with hypotheses, although only
the steps up to the current one were built.  In the first proof the others
are uninitialized memory, so this read indeterminate values: undefined
behaviour, which MemorySanitizer reports.  Copy only the steps built so
far, which are all the rearrangement uses.

The values read never reached the result, since only steps before the
current one are copied back.  Skipping the copies also makes verifying
/EXPLICIT proofs slightly faster.

The case open-msan-parseproof-uninit is now fixed: rename it
parseproof-uninit.
@benjub
benjub requested a review from david-a-wheeler October 2, 2026 11:13
@benjub benjub changed the title Fix UB in parseProof(): do not read uninitiliazed memory Fix UB in parseProof(): do not read uninitialized memory Oct 2, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant