Jens-G commented on PR #3786:
URL: https://github.com/apache/thrift/pull/3786#issuecomment-5608348885

   ### Code review
   
   Re-reviewed at `09c89d9cf`. Built the compiler and the standalone test suite 
from the PR head and from the merge base (`59a949c80`), compared behaviour on 
crafted inputs, and probed flex 2.6.4 directly with the scanner options this 
file uses.
   
   The rewrite does what the last round asked for, and it fixes one thing more 
than the ticket needed:
   
   - Unknown escapes stay invalid, and the diagnostic now names the offender 
and the way out: `"a\Tb"` → `Invalid escape sequence '\T'. Use \\ for a literal 
backslash.`
   - The dead `case EOF:` in the *escape* switch is genuinely fixed. `const 
string B = "abc\` at end of file reports `Bad escape character` on the base and 
`End of file while reading string at 1` at the head.
   - Dropping `%option noyywrap` is behaviour-neutral: flex expands that option 
to `#define yywrap() 1`, so the new `yywrap()` returns exactly what the macro 
returned. It is defined in the same file, so no `-lfl` dependency and no 
checked-in generated scanner to go stale.
   - The fixture and the new test file need no build-file changes — `file(GLOB 
${name}/*.c* ${name}/*.thrift)` in `tests/CMakeLists.txt` picks them up, and 
`EXTRA_DIST = … tests` covers `make dist`.
   - Accepted input is unchanged: `tutorial.thrift` through all 25 generators 
is byte-identical between the base compiler and the PR head.
   
   Found 1 issue.
   
   **The *outer* `case EOF:` (line 289) is still unreachable, and the loop it 
guards never terminates.**
   
   `yyinput()` returns `0` at end of input, never `-1` — I measured that with a 
standalone flex 2.6.4 scanner using this file's options, with and without 
`noyywrap`, and it keeps returning `0` on every subsequent call. So an 
unterminated string literal at end of file falls through to `default:`, pushes 
a NUL into `result`, and comes back for more. Two manifestations of the one 
defect:
   
   | input | base `59a949c80` | head `09c89d9cf` |
   |---|---|---|
   | `const string A = "abc` at EOF, from a file | hangs (10s timeout) | hangs 
(10s timeout) |
   | the same source via `yy_scan_bytes` | — | SIGSEGV |
   
   The crash is the memory-buffer path: with no `yyin`, the next `yyinput()` 
reaches `yy_get_next_buffer()`, which calls `fread` on a null `FILE*`.
   
   ```
   #0  __GI__IO_fread (buf=..., size=1, count=3, fp=0x0) at ./libio/iofread.c:37
   #1  yy_get_next_buffer () at thriftl.cc:1708
   #2  yyinput () at thriftl.cc:1841
   #3  yylex () at thriftl.ll:287
   ```
   
   That second row matters for this PR specifically: the new tests drive the 
lexer through `yy_scan_bytes`, so the first person who adds a case for an 
unterminated string takes the whole suite down with it.
   
   This is pre-existing — the base hangs identically, so it is not a regression 
from this PR. But the PR edits that exact line (`while read` → `while reading`, 
on a message that can never print), and `yyinput_reached_eof` is already there 
to tell the two cases apart. Four lines finish it:
   
   ```diff
      for(;;)
      {
   +    yyinput_reached_eof = false;
        int ch = yyinput();
        switch (ch) {
   -      case EOF:
   -        yyerror("End of file while reading string at %d\n", yylineno);
   -        exit(1);
   +      case 0:
   +        if (yyinput_reached_eof) {
   +          yyerror("End of file while reading string at %d\n", yylineno);
   +          exit(1);
   +        }
   +        result.push_back('\0');
   +        continue;
          case '\n':
   ```
   
   `result.push_back('\0')` keeps today's behaviour for a literal NUL inside a 
*closed* string — it lands in `result` and `strdup` truncates there, exactly as 
now.
   
   I have pushed that onto this branch as f8d01479b, together with two tests 
(an unterminated string at end of input, and a literal NUL inside a closed 
string). With it applied, `const string A = "abc` reports `End of file while 
reading string at 1` and exits 1, `const string N = "a<NUL>b"` still compiles 
unchanged, `tutorial.thrift` through all 25 generators stays byte-identical, 
and the suite is 22 cases / 326 assertions green, also under `--order rand`. 
Both tests fail (SIGSEGV) without the lexer change. Please squash it into your 
commit before this merges — and do shout if you would rather have it out again.
   
   Note the same dead `case EOF:` sits in the doc-comment block (line 139) and 
the multiline-comment block (line 182): `/* x` and `/** x` at end of file hang 
the compiler as well. Those are untouched by this PR and belong in their own 
ticket.
   
   ### Copilot's open comments
   
   Of the eight threads still marked current, one was worth acting on, and it 
is the finding above — reported twice, at `thriftl.ll:295` and `:305`, and now 
addressed by f8d01479b. The rest:
   
   - `parser_support.cc:68` (unchecked second `vsnprintf`, double newline) and 
`:54` (prefix printed before formatting is known to succeed) — both already 
fixed in this head; Copilot is re-reporting its own earlier round.
   - `parser_support.cc:55` and `:57` (`vsnprintf(nullptr, 0, …)` portability) 
— not a real constraint. That behaviour is required by C99 §7.19.6.5 and by 
C++11, and MSVC has conformed since VS2015. Test-only code besides.
   - `thriftl.ll:333` (`ch` signedness) — theoretical. `yyinput()` provably 
returns 0…255 here and `0` is handled separately, so `ch` in `default:` is 
1…255. Harmless to add the `unsigned char` cast if you want the belt as well as 
the braces.
   - `parser_support.cc:68` (newline normalised for stderr but not for the 
thrown message) — true, and the call sites in `thrifty.yy` and `main.cc` do 
lack the trailing `\n`, but nothing asserts the difference and it is test-only.
   
   Worth knowing when you read those: Copilot's last four runs all report 
*"couldn't run its full agentic review … timeout"*, so they ran degraded. That 
is why the same half-resolved finding came back three times.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to