Mercurial > illumos > illumos-gate
changeset 13171:84ccd9321eb7
6897938 zonecfg(1M) hemorrhages memory after Yacc processes commands with syntax errors
author | jv227347 <Jordan.Vaughan@Sun.com> |
---|---|
date | Tue, 17 Aug 2010 13:22:25 -0700 |
parents | 420a09381f6f |
children | bb6d135b32d1 |
files | usr/src/cmd/zonecfg/zonecfg.h usr/src/cmd/zonecfg/zonecfg_grammar.y usr/src/cmd/zonecfg/zonecfg_lex.l |
diffstat | 3 files changed, 180 insertions(+), 31 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/cmd/zonecfg/zonecfg.h Tue Aug 17 11:03:38 2010 -0700 +++ b/usr/src/cmd/zonecfg/zonecfg.h Tue Aug 17 13:22:25 2010 -0700 @@ -228,6 +228,12 @@ extern char *res_types[]; extern char *prop_types[]; +/* + * NOTE: Only Lex and YACC should use the following functions. + */ +extern void assert_no_unclaimed_tokens(void); +extern char *claim_token(char *); + #ifdef __cplusplus } #endif
--- a/usr/src/cmd/zonecfg/zonecfg_grammar.y Tue Aug 17 11:03:38 2010 -0700 +++ b/usr/src/cmd/zonecfg/zonecfg_grammar.y Tue Aug 17 13:22:25 2010 -0700 @@ -24,6 +24,14 @@ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. */ +/* + * This file defines zonecfg(1M)'s grammar. + * + * Reduction rules that consume TOKENs must invoke claim_token() immediately + * before freeing the TOKENs or adding them to data structures (e.g., cmd) that + * will be cleaned up when the parser finishes or encounters errors. + */ + #include <stdio.h> #include <strings.h> @@ -159,6 +167,11 @@ %% +/* + * NOTE: Each commands reduction rule must invoke assert_no_unclaimed_tokens() + * before it completes if it isn't processing an error. This ensures that + * reduction rules properly consume TOKENs. + */ commands: command terminator { if ($1 != NULL) { @@ -168,6 +181,7 @@ bzero(list, sizeof (list_property_t)); num_prop_vals = 0; } + assert_no_unclaimed_tokens(); return (0); } | command error terminator @@ -191,6 +205,7 @@ } | terminator { + assert_no_unclaimed_tokens(); return (0); } @@ -228,7 +243,7 @@ cmd = $$; $$->cmd_handler = &add_func; $$->cmd_argc = 1; - $$->cmd_argv[0] = $2; + $$->cmd_argv[0] = claim_token($2); $$->cmd_argv[1] = NULL; } | ADD resource_type @@ -269,7 +284,7 @@ cmd = $$; $$->cmd_handler = &cancel_func; $$->cmd_argc = 1; - $$->cmd_argv[0] = $2; + $$->cmd_argv[0] = claim_token($2); $$->cmd_argv[1] = NULL; } @@ -289,7 +304,7 @@ cmd = $$; $$->cmd_handler = &create_func; $$->cmd_argc = 1; - $$->cmd_argv[0] = $2; + $$->cmd_argv[0] = claim_token($2); $$->cmd_argv[1] = NULL; } | CREATE TOKEN TOKEN @@ -299,8 +314,8 @@ cmd = $$; $$->cmd_handler = &create_func; $$->cmd_argc = 2; - $$->cmd_argv[0] = $2; - $$->cmd_argv[1] = $3; + $$->cmd_argv[0] = claim_token($2); + $$->cmd_argv[1] = claim_token($3); $$->cmd_argv[2] = NULL; } | CREATE TOKEN TOKEN TOKEN @@ -310,9 +325,9 @@ cmd = $$; $$->cmd_handler = &create_func; $$->cmd_argc = 3; - $$->cmd_argv[0] = $2; - $$->cmd_argv[1] = $3; - $$->cmd_argv[2] = $4; + $$->cmd_argv[0] = claim_token($2); + $$->cmd_argv[1] = claim_token($3); + $$->cmd_argv[2] = claim_token($4); $$->cmd_argv[3] = NULL; } @@ -332,7 +347,7 @@ cmd = $$; $$->cmd_handler = &commit_func; $$->cmd_argc = 1; - $$->cmd_argv[0] = $2; + $$->cmd_argv[0] = claim_token($2); $$->cmd_argv[1] = NULL; } @@ -352,7 +367,7 @@ cmd = $$; $$->cmd_handler = &delete_func; $$->cmd_argc = 1; - $$->cmd_argv[0] = $2; + $$->cmd_argv[0] = claim_token($2); $$->cmd_argv[1] = NULL; } @@ -372,7 +387,7 @@ cmd = $$; $$->cmd_handler = &end_func; $$->cmd_argc = 1; - $$->cmd_argv[0] = $2; + $$->cmd_argv[0] = claim_token($2); $$->cmd_argv[1] = NULL; } @@ -392,7 +407,7 @@ cmd = $$; $$->cmd_handler = &exit_func; $$->cmd_argc = 1; - $$->cmd_argv[0] = $2; + $$->cmd_argv[0] = claim_token($2); $$->cmd_argv[1] = NULL; } @@ -412,7 +427,7 @@ cmd = $$; $$->cmd_handler = &export_func; $$->cmd_argc = 1; - $$->cmd_argv[0] = $2; + $$->cmd_argv[0] = claim_token($2); $$->cmd_argv[1] = NULL; } | EXPORT TOKEN TOKEN @@ -422,8 +437,8 @@ cmd = $$; $$->cmd_handler = &export_func; $$->cmd_argc = 2; - $$->cmd_argv[0] = $2; - $$->cmd_argv[1] = $3; + $$->cmd_argv[0] = claim_token($2); + $$->cmd_argv[1] = claim_token($3); $$->cmd_argv[2] = NULL; } @@ -443,7 +458,7 @@ cmd = $$; $$->cmd_handler = &help_func; $$->cmd_argc = 1; - $$->cmd_argv[0] = $2; + $$->cmd_argv[0] = claim_token($2); $$->cmd_argv[1] = NULL; } @@ -461,7 +476,7 @@ short_usage(CMD_INFO); (void) fputs("\n", stderr); usage(B_FALSE, HELP_RES_PROPS); - free($2); + free(claim_token($2)); YYERROR; } | INFO resource_type @@ -687,6 +702,7 @@ short_usage(CMD_REMOVE); (void) fputs("\n", stderr); usage(B_FALSE, HELP_RES_PROPS); + free(claim_token($2)); YYERROR; } | REMOVE resource_type @@ -705,7 +721,7 @@ $$->cmd_handler = &remove_func; $$->cmd_res_type = $3; $$->cmd_argc = 1; - $$->cmd_argv[0] = $2; + $$->cmd_argv[0] = claim_token($2); $$->cmd_argv[1] = NULL; } | REMOVE property_name property_value @@ -774,7 +790,7 @@ cmd = $$; $$->cmd_handler = &revert_func; $$->cmd_argc = 1; - $$->cmd_argv[0] = $2; + $$->cmd_argv[0] = claim_token($2); $$->cmd_argv[1] = NULL; } @@ -889,7 +905,7 @@ YYERROR; cmd = $$; $$->cmd_argc = 1; - $$->cmd_argv[0] = $2; + $$->cmd_argv[0] = claim_token($2); $$->cmd_argv[1] = NULL; $$->cmd_handler = &set_func; $$->cmd_prop_nv_pairs = 1; @@ -929,7 +945,7 @@ cmd = $$; $$->cmd_handler = &verify_func; $$->cmd_argc = 1; - $$->cmd_argv[0] = $2; + $$->cmd_argv[0] = claim_token($2); $$->cmd_argv[1] = NULL; } @@ -1046,7 +1062,7 @@ simple_prop_val: TOKEN { $$ = simple_prop_val_func($1); - free($1); + free(claim_token($1)); if ($$ == NULL) YYERROR; } @@ -1081,7 +1097,7 @@ complex_piece: property_name EQUAL TOKEN { $$ = complex_piece_func($1, $3, NULL); - free($3); + free(claim_token($3)); if ($$ == NULL) YYERROR; } @@ -1098,7 +1114,7 @@ | property_name EQUAL TOKEN COMMA complex_piece { $$ = complex_piece_func($1, $3, complex); - free($3); + free(claim_token($3)); if ($$ == NULL) YYERROR; }
--- a/usr/src/cmd/zonecfg/zonecfg_lex.l Tue Aug 17 11:03:38 2010 -0700 +++ b/usr/src/cmd/zonecfg/zonecfg_lex.l Tue Aug 17 13:22:25 2010 -0700 @@ -24,17 +24,37 @@ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. */ +#include <assert.h> #include <string.h> #include <libintl.h> #include "zonecfg.h" #include "zonecfg_grammar.tab.h" +/* + * This constant defines the number of entries added to unclaimed_tokens[] + * when it runs out of space. + */ +#define UNCLAIMED_TOKENS_BUFFER_GROWTH 4 + +/* + * Invariants: + * + * unclaimed_tokens == NULL IFF unclaimed_tokens_size == 0 + * unclaimed_tokens_size == 0 IFF num_unclaimed_tokens == 0 + */ +static char **unclaimed_tokens; /* TOKENs produced by Lex (see below) */ + /* but not claimed by YACC reduction */ + /* rules */ +static unsigned int unclaimed_tokens_size; /* size of unclaimed_tokens */ +static unsigned int num_unclaimed_tokens; /* number of unclaimed TOKENs */ + int lex_lineno = 1; /* line number for error reporting */ static int state = INITIAL; extern boolean_t cmd_file_mode; extern boolean_t saw_error; extern void yyerror(char *s); -char *safe_strdup(char *s); + +static char *create_token(char *s); %} %a 7000 @@ -323,29 +343,29 @@ <CSTATE>"," { return COMMA; } <TSTATE>[^ \t\n\";=\[\]\(\)]+ { - yylval.strval = safe_strdup(yytext); + yylval.strval = create_token(yytext); return TOKEN; } <LSTATE>[^ \t\n\",;=\[\]\(\)]+ { - yylval.strval = safe_strdup(yytext); + yylval.strval = create_token(yytext); return TOKEN; } <CSTATE>[^ \t\n\",;=\(\)]+ { - yylval.strval = safe_strdup(yytext); + yylval.strval = create_token(yytext); return TOKEN; } <TSTATE>\"[^\"\n]*[\"\n] { - yylval.strval = safe_strdup(yytext + 1); + yylval.strval = create_token(yytext + 1); if (yylval.strval[yyleng - 2] == '"') yylval.strval[yyleng - 2] = 0; return TOKEN; } <LSTATE>\"[^\"\n]*[\"\n] { - yylval.strval = safe_strdup(yytext + 1); + yylval.strval = create_token(yytext + 1); if (yylval.strval[yyleng - 2] == '"') yylval.strval[yyleng - 2] = 0; return TOKEN; @@ -370,8 +390,88 @@ %% +/* + * Assert that there are no unclaimed tokens. This function enforces the + * invariants mentioned at the top of this file. + */ +void +assert_no_unclaimed_tokens(void) +{ + assert(num_unclaimed_tokens == 0); + assert(unclaimed_tokens == NULL); + assert(unclaimed_tokens_size == 0); +} + +/* + * Claim the specified unclaimed TOKEN. YACC reduction rules that + * use TOKENs should invoke this function immediately before freeing the TOKENs + * or adding them to data structures that will be cleaned up when the YACC + * parser finishes or encounters errors. Reduction rules should only claim the + * TOKENs that they use. + * + * This function returns its argument but does not free its memory. + */ char * -safe_strdup(char *s) +claim_token(char *token) +{ + unsigned int index; + + /* + * Find the token in the list of unclaimed tokens. + */ + assert(num_unclaimed_tokens > 0); + for (index = 0; index < num_unclaimed_tokens; index++) { + if (unclaimed_tokens[index] == token) + break; + } + + /* + * Abort if we didn't find the token. + */ + assert(index != num_unclaimed_tokens); + + /* + * Replace the token with the last unclaimed token. + */ + num_unclaimed_tokens--; + unclaimed_tokens[index] = unclaimed_tokens[num_unclaimed_tokens]; + + /* + * Delete the list of unclaimed tokens if it's empty. + */ + if (num_unclaimed_tokens == 0) { + free(unclaimed_tokens); + unclaimed_tokens = NULL; + unclaimed_tokens_size = 0; + } + + return (token); +} + +/* + * Free all unclaimed TOKENs. This should only be invoked when the YACC + * parser encounters errors. + */ +static void +free_tokens(void) +{ + if (unclaimed_tokens != NULL) { + while (num_unclaimed_tokens > 0) + free(unclaimed_tokens[--num_unclaimed_tokens]); + free(unclaimed_tokens); + unclaimed_tokens = NULL; + unclaimed_tokens_size = 0; + } + assert_no_unclaimed_tokens(); +} + +/* + * Create a TOKEN from the specified string. The TOKEN is merely a duplicate + * of the specified string. TOKENs must be claimed by the YACC reduction rules + * that use them; see claim_token() above. + */ +char * +create_token(char *s) { char *result; @@ -379,12 +479,39 @@ yyerror("Out of memory"); exit(Z_ERR); } + + /* + * Add the new TOKEN to the list of unclaimed TOKENs. The list might + * have to be resized. + * + * Reduction rules should claim TOKENs via claim_token() (see above). + */ + if (num_unclaimed_tokens == unclaimed_tokens_size) { + char **new_unclaimed_tokens; + + unclaimed_tokens_size += UNCLAIMED_TOKENS_BUFFER_GROWTH; + new_unclaimed_tokens = (char **)realloc(unclaimed_tokens, + unclaimed_tokens_size * sizeof (char *)); + if (new_unclaimed_tokens == NULL) { + yyerror("Out of memory"); + free(result); + exit(Z_ERR); + } + unclaimed_tokens = new_unclaimed_tokens; + } + unclaimed_tokens[num_unclaimed_tokens] = result; + num_unclaimed_tokens++; return (result); } void yyerror(char *s) { + /* + * Ensure that we won't leak unclaimed tokens. + */ + free_tokens(); + /* feof(yyin) is not an error; anything else is, so we set saw_error */ if (yytext[0] == '\0') { if (!feof(yyin)) {