Skip to content

fix(geneepromfs): two-fix patch for token-overflow chain (CWE-170/209 info disclosure + CWE-787 OOB write) - #9

Open
GaltRanch wants to merge 2 commits into
nasa:masterfrom
AstrolexisAI:fix/parser-token-toolong-info-disclosure
Open

GaltRanch wants to merge 2 commits into
nasa:masterfrom
AstrolexisAI:fix/parser-token-toolong-info-disclosure

Conversation

@GaltRanch

@GaltRanch GaltRanch commented May 26, 2026

Copy link
Copy Markdown

Summary

Two related defects on the same error path in tools/geneepromfs/. The trigger is the same input — a single overlong token in an INPUT_FILE — so both are fixed in this PR.

# File / line Class Severity Commit
1 parser.c:121, 143 — String/Number Token "Too Long" branches CWE-170 (missing NUL) + CWE-209 (info disclosure in error message) Medium f5ea429
2 geneepromfs.c:419UglyExit central error formatter CWE-787 (out-of-bounds write via unbounded vsprintf into 256-byte BSS buffer) High dcd3006

Bug #2 was found by Inquisitor's SourceHunter agent after #1 was filed — the LLM correlated the unbounded vsprintf(Text, ...) in UglyExit with the ≥ 256-byte Parser.StringToken flowing in from the "Too Long" callers in parser.c, then confirmed via runtime evidence (the formatted error message printed on stdout exceeded the 256-byte Text[] sink intact, meaning vsprintf had written past the buffer end into adjacent BSS).


Bug #1 — Info disclosure via missing NUL terminator

tools/geneepromfs/parser.c:107-122 (String) and :127-143 (Number):

When the loop fills Parser.StringToken[0..255] to its full length (input had ≥ 256 consecutive token characters), no NUL is written before the subsequent UglyExit("...'%s' Too Long...", Parser.StringToken, ...). %s reads past the buffer into the adjacent struct field Parser.NumberToken, leaking up to four attacker-controlled bytes (the prior record's spare_bytes integer) into the error message printed on stdout.

Reproducer

$ cat > /tmp/leak.in <<'EOF'
/etc/hostname, host1.txt, 305419896, EEFS_ATTRIBUTE_NONE;
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA, host2.txt, 0, EEFS_ATTRIBUTE_NONE;
EOF

$ ./geneepromfs /tmp/leak.in /tmp/out.bin | xxd | tail -3
00000130: 41 78 56 34 12 27 20 54 6f 6f 20 4c 6f 6e 67 ...
                          ^ closing quote
              ^^^^^^^^^^^ 4 bytes of Parser.NumberToken (0x12345678 = spare_bytes 305419896 from record 1)

Fix (commit f5ea429)

+    Parser.StringToken[STRING_TOKEN_SIZE - 1] = '\0';
     UglyExit("File: %s Line: %lu: Error: String Token \'%s\' Too Long, ...",
              ..., Parser.StringToken, ...);

Same patch at the Number Token branch. After this, the reproducer above produces a clean AAAA...AAA' Too Long with no trailing leaked bytes.


Bug #2 — Out-of-bounds write via unbounded vsprintf

tools/geneepromfs/geneepromfs.c:419-429:

void UglyExit(char *Spec, ...) {
    va_list         Args;
    static char     Text[256];                  /* BSS, no canary */

    va_start(Args, Spec);
    vsprintf(Text, Spec, Args);                 /* UNBOUNDED */
    va_end(Args);

    printf("%s", Text);
    exit(1);
}

Every caller of UglyExit formats into this 256-byte BSS buffer with no length check. The "Too Long" callers from parser.c pass Parser.StringToken (up to 256 chars) plus Parser.Filename (up to 64 chars) plus the surrounding prefix/suffix — typical formatted output is ~310-380 bytes written into a 256-byte sink.

Because Text is static (BSS, not stack), no stack canary fires and the overflow silently corrupts whichever globals the linker placed after it. In our test build that includes Parser itself and adjacent CommandLineOptions_t state. Inputs are fully attacker-controlled — every byte past offset 256 of Text was supplied via the INPUT_FILE.

Reproducer (same input as Bug #1)

Build and run the same /tmp/leak.in. Pre-patch:

$ ./geneepromfs /tmp/leak.in /tmp/out.bin 2>&1 | wc -c
310

Post-patch (truncated to 255 bytes by vsnprintf):

$ ./geneepromfs /tmp/leak.in /tmp/out.bin 2>&1 | wc -c
255

Fix (commit dcd3006)

-    vsprintf(Text, Spec, Args);
+    /* CWE-787 fix: was vsprintf with no bound; ... see commit message ... */
+    vsnprintf(Text, sizeof(Text), Spec, Args);

One-line behavioural change. UglyExit's message is purely informational ("Error: ... Too Long, Max Length: 256") — truncating it at the buffer boundary is exactly the documented intent.


Why a public PR

nasa/eefs does not publish a SECURITY.md, and private vulnerability reporting is disabled on the repository (verified via the GitHub API). Given the bugs' nature (memory corruption via attacker-controlled input but no remote network surface — exploitation requires the operator to feed a hostile INPUT_FILE to the host-side geneepromfs tool), and the public availability of the source, a PR against the public repo is the appropriate channel.

If a maintainer wants the full disclosure bundle (PDF advisory, asciinema cast, GIF, MP4) before merging, happy to share via email — see contact below.

Verification

Both fixes were applied independently and tested:

Input Pre-patch Post commit f5ea429 Post commit dcd3006
Benign 1-record input parses OK parses OK parses OK
256-char token + prior numeric (Bug #1) leaks 0x12345678 after A's clean, no trailing leak clean, no trailing leak
257-char token (Bug #2 single-fix view) 310-byte stdout, BSS overflow leak gone, still 310 bytes (overflow remains until #2 fix) 255-byte stdout, no overflow

Bonus observations (out of scope for this PR)

While in the same file:

  • parser.c:54strncpy(Parser.Filename, Filename, MAX_FILENAME_SIZE) lacks the trailing [len-1]='\0'. Currently safe because Filename (an argv) is bounded by the OS, but a refactor that calls ParserOpen with an attacker-shaped path would re-introduce the same NUL-term gap as Bug Using EEFS on an 8 bit AVR ATmega, with 16 bit pointer arithmetic. #1.
  • parser.c:166, 190strcpy(InputParameters->...Filename, Parser.StringToken). Safe today because of the length check one line above, but a future move/removal of that check would re-introduce risk.

These are pre-existing patterns, not security findings on their own — only mentioned because a maintainer applying these fixes is in the right file to address them in the same pass.

Provenance

Discovered by Inquisitor, AstroLexis's autonomous binary security agent. Bug #1 surfaced via manual source review after VulnHunter (black-box probing) returned 0 findings. Bug #2 was discovered by Inquisitor's SourceHunter agent — the source-aware extension built specifically because VulnHunter missed Bug #1. SourceHunter wraps KCode's deterministic pattern engine (Fedora analogy — open-source community SAST), then layers LLM-driven correlation of source patterns with runtime evidence (the Red Hat analogy — paid commercial layer). It correctly identified that the vsprintf in UglyExit was the real memory-corruption sink, not the printf %s I'd hand-flagged in #1.


Bruno Aiub · AstroLexis · contact@astrolexis.space

Bruno Aiub added 2 commits May 26, 2026 19:09
…error printf (CWE-170/CWE-209)

When an INPUT_FILE supplies a token of exactly STRING_TOKEN_SIZE (256)
characters to either the String- or Number-token parser branch in
tools/geneepromfs/parser.c, the loop fills Parser.StringToken[0..255]
without ever writing a NUL terminator. The subsequent UglyExit("...'%s'
Too Long ...", Parser.StringToken, ...) then reads past the buffer end
into the adjacent struct field Parser.NumberToken, leaking up to four
bytes of internal program state into the error message printed on
stdout.

Parser is a global (.bss), and Parser.NumberToken is populated by the
preceding NUMBER token in the same input file (e.g. the spare_bytes
field of a prior record), making this fully attacker-controllable in
the documented usage of geneepromfs.

The fix is a one-line defensive NUL-write immediately before each of
the two "Too Long" UglyExit calls. This truncates the printed token
representation at the buffer boundary, consistent with the existing
error wording ("Too Long, Max Length: 256"). No behavioural change on
inputs that already exit through the normal early-out path.

Repro and full advisory available on request — happy to share the
AstroLexis bundle (asciinema cast + gif + mp4 + PDF) for verification.
…(CWE-787)

UglyExit() is the central error-reporting routine for the entire
geneepromfs tool. It declares a static char Text[256] in BSS and
formats every caller's printf-style arguments into it via vsprintf —
with NO length bound.

Several callers can produce formatted output exceeding 256 bytes:

  parser.c:121  "File: %s Line: %lu: Error: String Token '%s' Too Long,
                 Max Length: %lu\n"
  parser.c:143  same shape, Number Token variant

Both pass Parser.StringToken (up to 256 chars) plus Parser.Filename
(up to 64 chars) plus the surrounding prefix/suffix text — roughly
310-380 bytes of output written into a 256-byte sink. Because Text is
a global in BSS (not on the stack), no canary fires; the overflow
silently clobbers whatever sits next in the linker's BSS layout —
in practice the Parser struct itself and adjacent globals.

The runtime evidence is the entire formatted message appearing on
stdout intact (the printf("%s", Text) after the vsprintf reads from
the now-corrupted-but-still-readable BSS region). The overflow is
fully attacker-controlled: every byte past offset 256 of Text was a
character supplied via the INPUT_FILE.

Fix: replace vsprintf with vsnprintf(Text, sizeof(Text), Spec, Args).
The message is informational only — truncating it at the buffer
boundary is exactly the documented intent of "Max Length: 256". No
behavioural change for any input whose formatted form already fits.

Discovered by Inquisitor's SourceHunter agent (LLM correlated the
unbounded vsprintf in geneepromfs.c with the >=256-byte StringToken
flowing in from parser.c's "Too Long" path and confirmed the runtime
output exceeded the 256-byte sink).
@GaltRanch GaltRanch changed the title fix(geneepromfs): NUL-terminate StringToken before "Too Long" printf (CWE-170/CWE-209 info disclosure) fix(geneepromfs): two-fix patch for token-overflow chain (CWE-170/209 info disclosure + CWE-787 OOB write) May 26, 2026
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