From 2b7c317a06ff9bb79b49dc775f11103ddb927e57 Mon Sep 17 00:00:00 2001 From: Exberg Date: Sun, 23 Aug 2026 18:19:46 +0800 Subject: [PATCH] fix: initialize PreTypes sentinel to fix uninitialized read on stray #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 #242 --- check/fixes.frm | 14 ++++++++++++++ sources/startup.c | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/check/fixes.frm b/check/fixes.frm index fa21fc7b..61e9c522 100644 --- a/check/fixes.frm +++ b/check/fixes.frm @@ -5141,3 +5141,17 @@ Global F1 = E{`i' % 10}*E2*E3*E4; .end assert succeeded? *--#] Issue808 : +*--#[ Issue242 : +* An #endif without a matching #if must be diagnosed deterministically. +* Previously the preprocessor peeked at the uninitialized sentinel slot of +* the PreTypes stack (PreTypes[0] with NumPreTypes == 0), so the behavior +* depended on heap garbage (Valgrind: "Conditional jump or move depends on +* uninitialised value" in DoEndif, reported at +* https://github.com/form-dev/form/issues/242). +#ifdef `A' +#message A is defined +#endif +#endif +.end +assert preprocess_error?("#endif without corresponding #if") +*--#] Issue242 : diff --git a/sources/startup.c b/sources/startup.c index 126b7d3b..b444f498 100644 --- a/sources/startup.c +++ b/sources/startup.c @@ -1306,6 +1306,13 @@ void StartVariables(void) AP.MaxPreTypes = 10; AP.NumPreTypes = 0; AP.PreTypes = (int *)Malloc1(sizeof(int)*(AP.MaxPreTypes+1),"preprocessor types"); +/* + The sentinel slot must be initialized: with an empty stack the + preprocessor termination handlers (DoEndif, DoElse, DoElseif, ...) + peek at PreTypes[NumPreTypes] == PreTypes[0], and Malloc1 does not + zero the memory. +*/ + AP.PreTypes[0] = PRETYPENONE; AP.inside.buffer = 0; AP.inside.size = 0;