Jens Geyer created THRIFT-6212:
----------------------------------

             Summary: Compiler loops forever on an unterminated comment at end 
of file
                 Key: THRIFT-6212
                 URL: https://issues.apache.org/jira/browse/THRIFT-6212
             Project: Thrift
          Issue Type: Bug
          Components: Compiler (General)
            Reporter: Jens Geyer


h2. Symptom

A {{.thrift}} file whose last comment is never closed makes the compiler loop 
forever instead of reporting an error:

{code}
$ printf '/* unterminated comment' > c.thrift
$ thrift --gen cpp c.thrift        # never returns
$ printf '/** unterminated doc'    > d.thrift
$ thrift --gen cpp d.thrift        # never returns
{code}

Reproduced against master @ {{5c5e93e1e}} (the lexer is byte-identical to 
{{59a949c80}}, where it was measured). Both invocations were still running when 
a 10 second timeout killed them.

h2. Cause

Both comment scanners test {{yyinput()}} for {{EOF}}:

{code}
    int ch = yyinput();
    parsed.push_back(ch);
    switch (ch) {
      case EOF:
        yyerror("Unexpected end of file in multiline comment at %d\n", 
yylineno);
        exit(1);
{code}

flex's {{yyinput()}} returns {{0}} at end of input, never {{-1}}, and keeps 
returning {{0}} on every subsequent call. Measured with a standalone flex 2.6.4 
scanner using this file's own options, with and without {{noyywrap}}. So {{case 
EOF:}} never matches, the byte falls through to {{default:}}, 
{{parsed.push_back(ch)}} appends a NUL, and the loop asks for one more 
character forever, growing {{parsed}} without bound.

Two sites in {{compiler/cpp/src/thrift/thriftl.ll}}:

* line 136, the {{doctext_begin}} rule
* line 179, the {{multicm_begin}} rule

When the input comes from a memory buffer rather than a file there is no 
{{yyin}}, and the loop reaches {{fread()}} on a null {{FILE*}} instead of 
spinning:

{code}
#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
{code}

That path matters because the compiler test suite drives the lexer through 
{{yy_scan_bytes()}}, so a test case for an unterminated comment takes the whole 
suite down until this is fixed.

h2. Relationship to THRIFT-4244

The identical defect in the string-literal scanner is being fixed under 
THRIFT-4244, which introduces a {{yywrap()}} callback and a flag that tells 
real end of input from a literal NUL byte. The comment scanners were 
deliberately left out of that pull request's scope.

The fix here reuses that flag, so it depends on THRIFT-4244 landing first.

h2. Proposed fix

Replace {{case EOF:}} with {{case 0:}} guarded by the end-of-input flag, at 
both sites:

{code}
   while(state < 2)
   {
+    yyinput_reached_eof = false;
     int ch = yyinput();
     parsed.push_back(ch);
     switch (ch) {
-      case EOF:
-        yyerror("Unexpected end of file in multiline comment at %d\n", 
yylineno);
-        exit(1);
+      case 0:
+        if (yyinput_reached_eof) {
+          yyerror("Unexpected end of file in multiline comment at %d\n", 
yylineno);
+          exit(1);
+        }
+        state = 0;
+        break;
{code}

Guarding on the flag rather than treating every {{0}} as end of input keeps the 
current behaviour for a literal NUL byte inside a closed comment, which today 
lands in {{parsed}} and is either discarded ({{multicm_begin}}) or truncated by 
{{strdup()}} ({{doctext_begin}}).

Accepted input is unaffected either way: the change only reaches input that 
never terminates today.

h2. Scope

Compiler only. The lexer is shared by every language binding, so no generator 
changes are needed.

This description was AI-assisted (Claude Opus 5); every claim above was 
verified by execution against the named commits.




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to