]> git.draconx.ca Git - cdecl99.git/commitdiff
libcdecl: Fix memory leak when parsing e.g., int () int.
authorNick Bowler <nbowler@draconx.ca>
Mon, 24 Jul 2023 03:46:36 +0000 (23:46 -0400)
committerNick Bowler <nbowler@draconx.ca>
Fri, 28 Jul 2023 04:20:52 +0000 (00:20 -0400)
After the parser reduces the start symbol, it can still fail if the
scanner returns additional tokens.  In this case, nobody frees the
parse tree that was already output by the previously-executed parser
actions.

Solve this by simply freeing the output tree in the caller on error.

We make a slight adjustment to the "interactive" test case so that it
will tickle this particular issue.  When using "leak sanitizer", the
leak will be found and a nonzero exit status will be substituted,
failing the test.

The existing "command error messages" test also notices the problem when
using "leak sanitizer", but not because of the exit status: it only sees
text printed to stderr in the wrong format.

src/parse-decl.c
tests/general.at

index a31a37756427a7f096a26ab3c5718f90535eaef4..1268fc387810cbd2c7a1ef380b01bc09097e3342 100644 (file)
@@ -500,9 +500,9 @@ static int forall_declarators(struct cdecl *decl,
 
 static struct cdecl *do_parse(const char *str, int english_mode)
 {
+       struct cdecl *decl = NULL;
        YY_BUFFER_STATE state;
        yyscan_t scanner;
-       struct cdecl *decl;
 
 #if YYDEBUG
        extern int cdecl__yydebug;
@@ -514,8 +514,15 @@ static struct cdecl *do_parse(const char *str, int english_mode)
                return NULL;
 
        state = cdecl__yy_scan_string(str, scanner);
-       if (cdecl__yyparse(scanner, &decl) != 0)
+       if (cdecl__yyparse(scanner, &decl) != 0) {
+               /*
+                * If the input consists of a complete, valid declaration
+                * followed by some garbage, that parsed declaration will
+                * be output by the parser and we need to free it here.
+                */
+               cdecl__free(decl);
                decl = NULL;
+       }
        cdecl__yy_delete_buffer(state, scanner);
        cdecl__yylex_destroy(scanner);
 
index 498028e4c4b6e6d2cd3d3ada53778b16682a7184..bc22e44e5e00cb6e58f6c77d5b597cc7abaca718 100644 (file)
@@ -153,17 +153,26 @@ AT_CLEANUP
 AT_SETUP([cdecl99 interactive mode])
 
 AT_DATA([test.dat],
-[[explain int x;
+[[explain int () int
+explain int x;
 quit
 ]])
 
-AT_CHECK([cdecl99 --quiet --interactive <test.dat], [0], [stdout])
+AT_CHECK([cdecl99 --quiet --interactive <test.dat], [0], [stdout], [ignore])
 
 # If built with readline support, then the input commands (including their
 # trailing newlines) will be captured by AT_CHECK.  Otherwise, they are not:
 # the output just directly follows the prompt, and the final prompt will
 # not end with a newline.  Attempt to paper over these differences.
-AT_CHECK([echo >>stdout; sed '/> [[eq]]/d; s/^> //; /^$/d' stdout], [0],
+AT_DATA([check.sed],
+[[/> [eq]/d
+:loop
+s/^> //
+t loop
+/^$/d
+]])
+
+AT_CHECK([echo >>stdout; sed -f check.sed stdout], [0],
 [declare x as int
 ])