Skip to content

fix: initialize PreTypes sentinel to fix uninitialized read on stray #endif - #895

Open
Exberg wants to merge 1 commit into
form-dev:masterfrom
Exberg:fix/issue242-endif-uninit-sentinel
Open

fix: initialize PreTypes sentinel to fix uninitialized read on stray #endif#895
Exberg wants to merge 1 commit into
form-dev:masterfrom
Exberg:fix/issue242-endif-uninit-sentinel

Conversation

@Exberg

@Exberg Exberg commented Aug 23, 2026

Copy link
Copy Markdown

Summary

An extra #endif (no matching #if) made the preprocessor's termination
handlers (DoEndif, and the same peek pattern in DoElse, DoElseif,
DoEnddo, DoEndprocedure, ...) read AP.PreTypes[AP.NumPreTypes] with an
empty stack — i.e. PreTypes[0], a sentinel slot that was never
initialized. Malloc1 wraps malloc without zeroing (tools.c), so the
!= PRETYPEIF branch depended on heap garbage:

  • Valgrind reported Conditional jump or move depends on uninitialised value(s) in DoEndif (pre.c), exactly as in this issue's report.
  • Depending on the garbage value, the stray #endif could also be silently ignored (the non-reproducible case mentioned in the issue).

Fix

Initialize the sentinel at allocation time in startup.c:

AP.PreTypes[0] = PRETYPENONE;

This mirrors the existing sentinel initialization of the sibling stacks
(PreIfStack[0] = EXECUTINGIF in setfile.c, PreSwitchModes[0] = EXECUTINGPRESWITCH in pre.c). AddToPreTypes copies 0..MaxPreTypes when
it grows the array, so the sentinel is preserved across reallocations. No
behavioral change for correctly nested input: every handler pushes before the
slot can be read, and every matching pop restores NumPreTypes == 0.

With the fix, the exact reproducer from the issue now fails deterministically
and identically on every run:

test242.frm Line 5 ==> #endif without corresponding #if
Program terminating at test242.frm Line 1 -->

Regression test

Added Issue242 to check/fixes.frm: a stray #endif must always be
diagnosed (assert preprocess_error?("#endif without corresponding #if")).

Test plan

Built locally on macOS arm64 (./configure --disable-float && make -C sources form; MPFR not available on this machine — see note below).

  • Issue reproducer: diagnostic now deterministic across 5 repeated runs
  • ./check.rb ./sources/form Issue242 — 1 test, 1 assertion, passes
  • Full default suite: 543 tests, 54 failures — every failure is a float-feature test (#StartFloat, mzv_, Evaluate, chop, torat, ...), reproduced identically (same 54 tests, diffed) on a pristine master worktree built with the same --disable-float configuration. They are a pre-existing consequence of building without GMP+MPFR on this machine, unrelated to this change. GitHub CI builds with the full feature set.

Note for reviewers: I could not run the Valgrind matrix locally (Linux-only
runner feature); the fix removes the uninitialized read by construction and
the deterministic test covers the observable behavior.

Closes #242

…#endif

An extra #endif (and likewise #else/#elseif/#endprocedure with an empty
preprocessor nesting stack) made DoEndif and friends read
AP.PreTypes[AP.NumPreTypes] == PreTypes[0]. That sentinel slot was never
initialized -- Malloc1 wraps malloc without zeroing -- so the error branch
depended on heap garbage. Valgrind reported "Conditional jump or move
depends on uninitialised value(s)" in DoEndif, and depending on the
garbage value the extra #endif was sometimes silently ignored.

Initialize PreTypes[0] to PRETYPENONE at allocation time in startup,
mirroring the existing PreIfStack[0] and PreSwitchModes[0] sentinels, so
the stack is deterministic with or without garbage on the heap.

Add a deterministic regression test (check/fixes.frm, Issue242): a stray
#endif must always be diagnosed as "#endif without corresponding #if".

Fixes form-dev#242
@coveralls

Copy link
Copy Markdown

Coverage Status

coverage: 65.037% (-0.01%) from 65.048% — Exberg:fix/issue242-endif-uninit-sentinel into form-dev:master

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.

Memory error for extra #endif

2 participants