Found in the July 2026 AI-assisted code review (Claude Code). Four latent defects in src/mwrap.l:
-
Negative-index scan in fname_scan_line (~line 43): while (s[name_start] > 0 && is_name_char(s[name_start])) --name_start; tests the character, not the index — should be name_start >= 0. With @functionfoo (no space) the loop walks s[-1], s[-2], … reading out of bounds and producing a garbage .m filename. Latent because normal input has a space after @function.
-
static char namebuf[256] overflow (~line 31, author-flagged FIXME): @function names longer than ~253 characters overflow the static buffer. Size-check or allocate dynamically.
-
Unterminated string rule \'[^'\n]*['\n] (~line 223) accepts a newline as the string terminator, embeds it in the token, and does not increment linenum — every later error message in the file is off by one. Split into a proper unterminated-string case. Note the Python port currently stops at end of line here (known divergence); pick one behavior and mirror it.
-
Dangling outfp in -mb mode (~lines 99-109): a bare @ line (no filename) or @function followed immediately by newline does fclose(outfp) without setting outfp = NULL; subsequent if (outfp) fprintf(...) writes through a closed FILE* (UB). Also the FSTATE fopen-failure message prints yytext (the whole line) instead of the computed filename (~line 169).
Any observable behavior change must be mirrored in python/mwrap_lexer.py; testing/test_python.sh byte-compares the implementations.
Found in the July 2026 AI-assisted code review (Claude Code). Four latent defects in
src/mwrap.l:Negative-index scan in
fname_scan_line(~line 43):while (s[name_start] > 0 && is_name_char(s[name_start])) --name_start;tests the character, not the index — should bename_start >= 0. With@functionfoo(no space) the loop walkss[-1],s[-2], … reading out of bounds and producing a garbage.mfilename. Latent because normal input has a space after@function.static char namebuf[256]overflow (~line 31, author-flaggedFIXME):@functionnames longer than ~253 characters overflow the static buffer. Size-check or allocate dynamically.Unterminated string rule
\'[^'\n]*['\n](~line 223) accepts a newline as the string terminator, embeds it in the token, and does not incrementlinenum— every later error message in the file is off by one. Split into a proper unterminated-string case. Note the Python port currently stops at end of line here (known divergence); pick one behavior and mirror it.Dangling
outfpin-mbmode (~lines 99-109): a bare@line (no filename) or@functionfollowed immediately by newline doesfclose(outfp)without settingoutfp = NULL; subsequentif (outfp) fprintf(...)writes through a closedFILE*(UB). Also the FSTATE fopen-failure message printsyytext(the whole line) instead of the computed filename (~line 169).Any observable behavior change must be mirrored in
python/mwrap_lexer.py;testing/test_python.shbyte-compares the implementations.