Produced with LLM assistance.
build_relations() in usr.bin/yacc/lalr.c sizes 'edge' for ngotos+1
shorts:
edge = NEW2(ngotos + 1, short);
But edge is a per-iteration scratch buffer (nedges resets at line
365), and the writes per outer iteration can reach the sum of
rhs lengths over rules sharing one lhs, which exceeds ngotos.
14-byte reproducer:
$ cat > /tmp/min.y <<EOT
%%
t:t
t:t,t:t
EOT
$ env MALLOC_OPTIONS=CFGJU yacc /tmp/min.y
yacc(NNNNN) in free(): canary corrupted 0x...[4]@4/16
Abort trap (core dumped)
Reproduced on OpenBSD 7.9 amd64, i386 and arm64. Found by AFL++
fuzzing. Patched yacc produces byte-identical output to stock on
every .y file in the base tree.
Index: usr.bin/yacc/lalr.c
===================================================================
--- usr.bin/yacc/lalr.c
+++ usr.bin/yacc/lalr.c
@@ -358,7 +358,8 @@ build_relations(void)
short **new_includes;
includes = NEW2(ngotos, short *);
- edge = NEW2(ngotos + 1, short);
+ /* per-iteration scratch; bounded by total grammar items */
+ edge = NEW2(nitems + 1, short);
states = NEW2(maxrhs + 1, short);
for (i = 0; i < ngotos; i++) {