# Torturing Bison. -*- Autotest -*- # Copyright (C) 2001-2002, 2004-2007, 2009-2012 Free Software # Foundation, Inc. # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . AT_BANNER([[Torture Tests.]]) # AT_INCREASE_DATA_SIZE(SIZE) # --------------------------- # Try to increase the data size to SIZE KiB if possible. m4_define([AT_INCREASE_DATA_SIZE], [data_limit=`(ulimit -S -d) 2>/dev/null` case $data_limit in [[0-9]]*) if test "$data_limit" -lt $1; then AT_CHECK([ulimit -S -d $1 || exit 77]) ulimit -S -d $1 fi esac]) ## ------------------------------------- ## ## Creating a large artificial grammar. ## ## ------------------------------------- ## # AT_DATA_TRIANGULAR_GRAMMAR(FILE-NAME, SIZE) # ------------------------------------------- # Create FILE-NAME, containing a self checking parser for a huge # triangular grammar. m4_define([AT_DATA_TRIANGULAR_GRAMMAR], [AT_DATA([[gengram.pl]], [[#! /usr/bin/perl -w use strict; my $max = $ARGV[0] || 10; print < #include static int yylex (void); static void yyerror (const char *msg); %} %union { int val; }; %token END "end" %type exp input EOF for my $size (1 .. $max) { print "%token t$size $size \"$size\"\n"; }; print < $max) return 0; else if (inner > outer) { inner = 1; ++outer; return END; } return inner++; } static void yyerror (const char *msg) { fprintf (stderr, "%s\\n", msg); } int main (void) { yydebug = !!getenv ("YYDEBUG"); return yyparse (); } EOF ]]) AT_CHECK([perl -w ./gengram.pl $2 || exit 77], 0, [stdout]) mv stdout $1 ]) ## -------------- ## ## Big triangle. ## ## -------------- ## AT_SETUP([Big triangle]) # I have been able to go up to 2000 on my machine. # I tried 3000, a 29Mb grammar file, but then my system killed bison. # With 500 and the new parser, which consume far too much memory, # it gets killed too. Of course the parser is to be cleaned. AT_DATA_TRIANGULAR_GRAMMAR([input.y], [200]) AT_BISON_CHECK_NO_XML([-v -o input.c input.y]) AT_COMPILE([input]) AT_PARSER_CHECK([./input]) AT_CLEANUP # AT_DATA_HORIZONTAL_GRAMMAR(FILE-NAME, SIZE) # ------------------------------------------- # Create FILE-NAME, containing a self checking parser for a huge # horizontal grammar. m4_define([AT_DATA_HORIZONTAL_GRAMMAR], [AT_DATA([[gengram.pl]], [[#! /usr/bin/perl -w use strict; my $max = $ARGV[0] || 10; print < #include static int yylex (void); static void yyerror (const char *msg); %} %token EOF for my $size (1 .. $max) { print " t$size $size \"$size\"\n"; }; print < # include # include static int yylex (void); static void yyerror (const char *msg); %} %union { int val; }; %type input exp %token token EOF print wrap ("%type ", " ", map { "n$_" } (1 .. $max)), "\n"; print "%token\n"; for my $count (1 .. $max) { print " t$count $count \"$count\"\n"; }; print < $max) { if (counter++ != $max + 1) abort (); return 0; } if (return_token) { return_token = 0; return token; } return_token = 1; return counter++; } static void yyerror (const char *msg) { fprintf (stderr, "%s\\n", msg); } int main (void) { yydebug = !!getenv ("YYDEBUG"); return yyparse (); } EOF ]]) AT_CHECK([perl -w ./gengram.pl $2 || exit 77], 0, [stdout]) mv stdout $1 ]) ## ------------------------ ## ## Many lookahead tokens. ## ## ------------------------ ## AT_SETUP([Many lookahead tokens]) AT_DATA_LOOKAHEAD_TOKENS_GRAMMAR([input.y], [1000]) # GNU m4 requires about 70 MiB for this test on a 32-bit host. # Ask for 200 MiB, which should be plenty even on a 64-bit host. AT_INCREASE_DATA_SIZE(204000) AT_BISON_CHECK([-v -o input.c input.y]) AT_COMPILE([input]) AT_PARSER_CHECK([./input]) AT_CLEANUP # AT_DATA_STACK_TORTURE(C-PROLOGUE, [BISON-DECLS]) # ------------------------------------------------ # A parser specialized in torturing the stack size. m4_define([AT_DATA_STACK_TORTURE], [# A grammar of parens growing the stack thanks to right recursion. # exp: AT_DATA([input.y], [[%{ #include #include #include #include ]$1[ static int yylex (void); static void yyerror (const char *msg); %} ]$2[ %error-verbose %debug %token WAIT_FOR_EOF %% exp: WAIT_FOR_EOF exp | ; %% static void yyerror (const char *msg) { fprintf (stderr, "%s\n", msg); } static int yylex (void) { if (yylval < 0) abort (); if (yylval--) return WAIT_FOR_EOF; else return EOF; } int main (int argc, const char **argv) { char *endp; YYSTYPE yylval_init; if (argc != 2) abort (); yylval_init = strtol (argv[1], &endp, 10); if (! (argv[1] != endp && 0 <= yylval_init && yylval_init <= INT_MAX && errno != ERANGE)) abort (); yydebug = 1; { int count; int status; ]m4_bmatch([$2], [%push-], [[ yypstate *ps = yypstate_new (); ]])[ for (count = 0; count < 2; ++count) { int new_status; yylval = yylval_init; ]m4_bmatch([$2], [%push-], [[ new_status = yypull_parse (ps); ]], [[ new_status = yyparse (); ]])[ if (count > 0 && new_status != status) abort (); status = new_status; } ]m4_bmatch([$2], [%push-], [[ yypstate_delete (ps); ]])[ return status; } } ]]) AT_BISON_CHECK([-o input.c input.y]) AT_COMPILE([input]) ]) ## -------------------------------------- ## ## Exploding the Stack Size with Alloca. ## ## -------------------------------------- ## AT_SETUP([Exploding the Stack Size with Alloca]) m4_pushdef([AT_USE_ALLOCA], [[ #if (defined __GNUC__ || defined __BUILTIN_VA_ARG_INCR \ || defined _AIX || defined _MSC_VER || defined _ALLOCA_H) # define YYSTACK_USE_ALLOCA 1 #endif ]]) AT_DATA_STACK_TORTURE([AT_USE_ALLOCA]) # Below the limit of 200. AT_PARSER_CHECK([./input 20], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) # Two enlargements: 2 * 2 * 200. AT_PARSER_CHECK([./input 900], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) # Fails: beyond the limit of 10,000 (which we don't reach anyway since we # multiply by two starting at 200 => 5120 is the last possible). AT_PARSER_CHECK([./input 10000], 2, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) # The push parser can't use alloca since the stacks can't be locals. This test # just helps guarantee we don't let the YYSTACK_USE_ALLOCA feature affect # push parsers. AT_DATA_STACK_TORTURE([AT_USE_ALLOCA], [[%define api.push-pull both ]]) AT_PARSER_CHECK([./input 20], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) AT_PARSER_CHECK([./input 900], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) AT_PARSER_CHECK([./input 10000], 2, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) m4_popdef([AT_USE_ALLOCA]) AT_CLEANUP ## -------------------------------------- ## ## Exploding the Stack Size with Malloc. ## ## -------------------------------------- ## AT_SETUP([Exploding the Stack Size with Malloc]) m4_pushdef([AT_USE_ALLOCA], [[#define YYSTACK_USE_ALLOCA 0]]) AT_DATA_STACK_TORTURE([AT_USE_ALLOCA]) # Below the limit of 200. AT_PARSER_CHECK([./input 20], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) # Two enlargements: 2 * 2 * 200. AT_PARSER_CHECK([./input 900], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) # Fails: beyond the limit of 10,000 (which we don't reach anyway since we # multiply by two starting at 200 => 5120 is the possible). AT_PARSER_CHECK([./input 10000], 2, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) AT_DATA_STACK_TORTURE([AT_USE_ALLOCA], [[%define api.push-pull both ]]) AT_PARSER_CHECK([./input 20], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) AT_PARSER_CHECK([./input 900], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) AT_PARSER_CHECK([./input 10000], 2, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) m4_popdef([AT_USE_ALLOCA]) AT_CLEANUP