Consider the program below.
Building and running this segfaults:
% flex -o flex-stack.c flex-stack.lex
% cc -o flex-stack flex-stack.c
% ./flex-stack "one two"
word:<one>
zsh: segmentation fault ./flex-stack "one two"
Looking in the debugger:
% lldb ./flex-stack
(lldb) target create "./flex-stack"
Current executable set to '/checkouts/me/codeberg/beastie/scratch/flex-stack' (arm64).
(lldb) proc lau "one two"
Process 27897 launched: '/checkouts/me/codeberg/beastie/scratch/flex-stack' (arm64)
word:<one>
Process 27897 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0xfffffffffffffffc)
frame #0: 0x0000000100000f18 flex-stack`yy_top_state at flex-stack.c:1588:13
1585 }
1586
1587 static int yy_top_state (void)
-> 1588 {
1589 return (yy_start_stack)[(yy_start_stack_ptr) - 1];
1590 }
1591
Target 0: (flex-stack) stopped.
(lldb) p yy_start_stack_ptr
(int) 0
(lldb) ^D
Ie, there is a stack underflow when yy_top_state is called when no start-state has been pushed. I would expect yy_top_state to handle this OK, and return 0/INITIAL in this case (if (yy_start_stack_ptr == 0) return 0; perhaps?). In the real case, I was calling yy_top_state in an error handler, hoping to give the caller some suitable feedback.
The same thing happens with %option reentrant, though with yyguts_t involved.
Versions, etc:
% flex -V
flex 2.6.4
% uname -a
Darwin PHAS-MACDESKE207E9 25.6.0 Darwin Kernel Version 25.6.0: Sat Jul 11 15:24:35 PDT 2026; root:xnu-12377.161.13~4/RELEASE_ARM64_T8103 arm64
% cc --version
Apple clang version 21.0.0 (clang-2100.1.1.101)
Target: arm64-apple-darwin25.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
This is specifically the 2.6.4 built from the sources here, rather than the v2.6.4 that comes with macOS (though it displays the same behaviour).
ALPHABETIC [a-zA-Z]
WS [^a-zA-Z!]
%option noyywrap stack
%%
{ALPHABETIC}+ {
printf("word:<%s>\n", yytext);
printf("stack %d\n", yy_top_state()); // <----- note
return 1;
}
{WS}+ {
return 2;
}
%%
int main(int argc, char** argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s string\n", argv[0]);
exit(1);
}
yy_scan_string(argv[1]);
int token;
while ((token = yylex()) != 0) {
printf("-> %d\n", token);
}
}
Consider the program below.
Building and running this segfaults:
Looking in the debugger:
Ie, there is a stack underflow when
yy_top_stateis called when no start-state has been pushed. I would expectyy_top_stateto handle this OK, and return 0/INITIAL in this case (if (yy_start_stack_ptr == 0) return 0;perhaps?). In the real case, I was callingyy_top_statein an error handler, hoping to give the caller some suitable feedback.The same thing happens with
%option reentrant, though withyyguts_tinvolved.Versions, etc:
This is specifically the 2.6.4 built from the sources here, rather than the v2.6.4 that comes with macOS (though it displays the same behaviour).