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
Conversation
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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.parser.c:121, 143— String/Number Token "Too Long" branchesf5ea429geneepromfs.c:419—UglyExitcentral error formatterdcd3006Bug #2 was found by Inquisitor's SourceHunter agent after #1 was filed — the LLM correlated the unbounded
vsprintf(Text, ...)inUglyExitwith the ≥ 256-byteParser.StringTokenflowing in from the "Too Long" callers in parser.c, then confirmed via runtime evidence (the formatted error message printed on stdout exceeded the 256-byteText[]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 subsequentUglyExit("...'%s' Too Long...", Parser.StringToken, ...).%sreads past the buffer into the adjacent struct fieldParser.NumberToken, leaking up to four attacker-controlled bytes (the prior record'sspare_bytesinteger) into the error message printed on stdout.Reproducer
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 Longwith no trailing leaked bytes.Bug #2 — Out-of-bounds write via unbounded vsprintf
tools/geneepromfs/geneepromfs.c:419-429:Every caller of
UglyExitformats into this 256-byte BSS buffer with no length check. The "Too Long" callers fromparser.cpassParser.StringToken(up to 256 chars) plusParser.Filename(up to 64 chars) plus the surrounding prefix/suffix — typical formatted output is ~310-380 bytes written into a 256-byte sink.Because
Textisstatic(BSS, not stack), no stack canary fires and the overflow silently corrupts whichever globals the linker placed after it. In our test build that includesParseritself and adjacentCommandLineOptions_tstate. Inputs are fully attacker-controlled — every byte past offset 256 ofTextwas supplied via the INPUT_FILE.Reproducer (same input as Bug #1)
Build and run the same
/tmp/leak.in. Pre-patch:Post-patch (truncated to 255 bytes by
vsnprintf):Fix (commit
dcd3006)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/eefsdoes not publish aSECURITY.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-sidegeneepromfstool), 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:
0x12345678after A'sBonus observations (out of scope for this PR)
While in the same file:
parser.c:54—strncpy(Parser.Filename, Filename, MAX_FILENAME_SIZE)lacks the trailing[len-1]='\0'. Currently safe becauseFilename(an argv) is bounded by the OS, but a refactor that callsParserOpenwith 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, 190—strcpy(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
vsprintfinUglyExitwas the real memory-corruption sink, not theprintf%sI'd hand-flagged in #1.—
Bruno Aiub · AstroLexis · contact@astrolexis.space