westes / flex

The Fast Lexical Analyzer - scanner generator for lexing in C and C++
Other
3.63k stars 536 forks source link

Infinite loop hang up in YY_INPUT macro if error occurs in reading a file stream #681

Closed HattonLe closed 1 month ago

HattonLe commented 1 month ago

YY_INPUT macro never exits the while loop if reading a file causes an error!

The fault occurs within the YY_INPUT macro so I can't step into it to debug easily, but looking thru the source files in this repo the code at fault is in cpp-flex.skl, line 1602 - "while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) "

This code will just sit there forever saying I've got an error, thereby hanging your application.

Mightyjo commented 1 month ago

The code in that while loop will break out for any error other that EINTR. That includes running out of bytes to read from the file. In this case, EINTR means fread's underlying system call was interrupted by the kernel, but we can try it again.

Under normal conditions, you'll go through the loop about file_len / max_size times. If you've opened a character device or something similar you probably won't get an EOF so your fread will be block forever waiting for bytes.

Can you share the code for your scanner?

westes commented 1 month ago

When reporting that your scanner encounters an error, please provide the scanner. Better yet, a simplified version that only contains the failing code -- a minimal example. Otherwise we cannot reproduce the problem. Nor can we tell if the problem has been fixed by any changes that may be introduced.

HattonLe commented 1 month ago

I expanded the macro so I could gdb into it... to find that fread() just sits there hanging. In my main() code I do the following

fd = open up a test text file;
yyset_in(fd);
yyparse();

But my test file was missing causing fd to be NULL which was 'causing' the fread() hang. If I put my test file in its proper place the code all works.

But digging into this problem scenario further ... I called yyset_in(NULL) but inside the call to yyparse() it eventually does the following code....

        if ( ! (yy_start) )
            (yy_start) = 1; /* first start state */

        if ( ! yyin )
            yyin = stdin;

BRAIN FADE... yyparser() defaults to stdin if yyin is NULL.... so due to my error yyparser decided to point itself to stdin where it would quite happy sit there hanging for eternity, instead of warning me I had a NULL yyin.

HattonLe commented 1 month ago

Would be nice if the parser warned you of trying to use a NULL yyin.

Mightyjo commented 1 month ago

You're right, that's annoying. We may have lost a guard check. I'll take a look.