From: Nick Bowler Date: Mon, 24 Jul 2023 03:46:36 +0000 (-0400) Subject: libcdecl: Fix memory leak when parsing e.g., int () int. X-Git-Tag: v1.3~95 X-Git-Url: https://git.draconx.ca/gitweb/cdecl99.git/commitdiff_plain/21bc2db0ec83b235caec38d42f4d0812f473d766 libcdecl: Fix memory leak when parsing e.g., int () int. 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. --- diff --git a/src/parse-decl.c b/src/parse-decl.c index a31a377..1268fc3 100644 --- a/src/parse-decl.c +++ b/src/parse-decl.c @@ -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); diff --git a/tests/general.at b/tests/general.at index 498028e..bc22e44 100644 --- a/tests/general.at +++ b/tests/general.at @@ -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 >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 ])