yacc: fix push parser

When a pstate is used for multiple successive parses, some state may
leak from one run into the following one.  That was introduced in
330552ea49 "yacc.c: push: don't clear
the parser state when accepting/rejecting".

Reported by Ryan <dev@splintermail.com>
https://lists.gnu.org/r/bug-bison/2021-03/msg00000.html

* data/skeletons/yacc.c (yypush_parse): We reusing a pstate from a
previous run, do behave as if it were the first run.
* tests/push.at (Pstate reuse): Check this.
This commit is contained in:
Akim Demaille
2021-03-07 10:01:53 +01:00
parent a774839ca8
commit cf899f7a7c
3 changed files with 139 additions and 8 deletions

8
NEWS
View File

@@ -4,11 +4,17 @@ GNU Bison NEWS
** Bug fixes ** Bug fixes
*** Reused Push Parsers
When a push-parser state structure is used for multiple parses, it was
possible for some state to leak from one run into the following one.
*** Fix Table Generation *** Fix Table Generation
In some very rare conditions, when there are many useless tokens, it was In some very rare conditions, when there are many useless tokens, it was
possible to generate incorrect parsers. possible to generate incorrect parsers.
* Noteworthy changes in release 3.7.5 (2021-01-24) [stable] * Noteworthy changes in release 3.7.5 (2021-01-24) [stable]
** Bug fixes ** Bug fixes
@@ -290,7 +296,7 @@ GNU Bison NEWS
parse errors, since `yynerrs` was also reset. This can be especially parse errors, since `yynerrs` was also reset. This can be especially
troublesome when used in autocompletion, since a parser with error troublesome when used in autocompletion, since a parser with error
recovery would suggest (irrelevant) expected tokens even if there were recovery would suggest (irrelevant) expected tokens even if there were
failure. failures.
Now the parser state can be examined when parsing is finished. The parser Now the parser state can be examined when parsing is finished. The parser
state is reset when starting a new parse. state is reset when starting a new parse.

View File

@@ -1573,14 +1573,16 @@ yyparse (]m4_ifset([b4_parse_param], [b4_formals(b4_parse_param)], [void])[)]])[
switch (yyps->yynew) switch (yyps->yynew)
{ {
case 2:
yypstate_clear (yyps);
goto case_0;
case_0:
case 0: case 0:
yyn = yypact[yystate]; yyn = yypact[yystate];
goto yyread_pushed_token; goto yyread_pushed_token;
case 2:
yypstate_clear (yyps);
break;
default:
break;
}]])[ }]])[
YYDPRINTF ((stderr, "Starting parse\n")); YYDPRINTF ((stderr, "Starting parse\n"));

View File

@@ -25,7 +25,7 @@ AT_BANNER([[Push Parsing Tests]])
AT_SETUP([[Memory Leak for Early Deletion]]) AT_SETUP([[Memory Leak for Early Deletion]])
# Requires Valgrind. # Requires Valgrind.
AT_BISON_OPTION_PUSHDEFS AT_BISON_OPTION_PUSHDEFS([%define api.push-pull push])
AT_DATA_GRAMMAR([[input.y]], AT_DATA_GRAMMAR([[input.y]],
[[ [[
%{ %{
@@ -144,7 +144,7 @@ AT_CLEANUP
AT_SETUP([[Unsupported Skeletons]]) AT_SETUP([[Unsupported Skeletons]])
AT_BISON_OPTION_PUSHDEFS AT_BISON_OPTION_PUSHDEFS([%define api.push-pull push])
AT_DATA([[input.y]], AT_DATA([[input.y]],
[[%glr-parser [[%glr-parser
%define api.push-pull push %define api.push-pull push
@@ -158,3 +158,126 @@ AT_BISON_CHECK([[input.y]], [[1]], [],
]]) ]])
AT_CLEANUP AT_CLEANUP
## -------------- ##
## Pstate reuse. ##
## -------------- ##
AT_SETUP([[Pstate reuse]])
# Make sure that when a single pstate is used for multiple successive
# parses, no state from a previous run leaks into the following one.
#
# See https://lists.gnu.org/r/bug-bison/2021-03/msg00000.html.
AT_BISON_OPTION_PUSHDEFS([%define api.push-pull push])
AT_DATA_GRAMMAR([[input.y]],
[[%code top {
#include <stdlib.h>
#include <string.h>
static char *string_concat (char *a, char *b);
]AT_YYERROR_DECLARE[
}
%define parse.trace
%define api.pure full
%define api.push-pull push
%expect 0
%union {
char *sval;
};
%destructor { free ($$); } <sval>
%printer { fprintf (yyo, "%s", $$); } <sval>
%token <sval> RAW
%token EOL
%type <sval> text
%%
line
: text EOL { printf ("text: %s\n", $1); free ($1); YYACCEPT; };
text
: RAW { $$ = $1; }
| text RAW { $$ = string_concat ($1, $2); }
;
%%
]AT_YYERROR_DEFINE[
static char *
string_concat (char *a, char *b)
{
size_t la = strlen (a);
size_t lb = strlen (b);
char *res = YY_CAST (char *, malloc (la + lb + 1));
strcpy (res, a);
strcpy (res + la, b);
free (a);
free (b);
return res;
}
static int
push (yypstate *ps, yytoken_kind_t kind, const char *str)
{
YYSTYPE lval;
lval.sval = str ? strdup (str) : YY_NULLPTR;
switch (yypush_parse (ps, kind, &lval))
{
case 0:
return 0;
case YYPUSH_MORE:
// parsing incomplete, but valid; parser not reset
return 0;
case 1:
// YYABORT or syntax invalid; parser is reset
fprintf (stderr, "invalid input, but no error was thrown\n");
return 1;
case 2:
// memory exhaustion; parser is reset
fprintf (stderr, "memory exhaustion during yypush_parse\n");
return 1;
}
return 1;
}
int
main (void)
{
yydebug = !!getenv ("YYDEBUG");
yypstate *ps = yypstate_new ();
#define PUSH(Kind, Val) \
do { \
if (push (ps, Kind, Val)) \
return 1; \
} while (0)
PUSH (RAW, "te");
PUSH (RAW, "xt");
PUSH (EOL, YY_NULLPTR);
PUSH (RAW, "te");
PUSH (RAW, "xt");
PUSH (EOL, YY_NULLPTR);
yypstate_delete (ps);
return 0;
}
]])
AT_FULL_COMPILE([input])
AT_CHECK([./input], 0,
[[text: text
text: text
]])
AT_BISON_OPTION_POPDEFS
AT_CLEANUP