ARB prog parser: Fix epic memory leak in lexer / parser interface

Anything that matched IDENTIFIER was strdup'ed and returned to the
parser.  However, almost every case of IDENTIFIER in the parser just
dropped the returned string on the floor.  Every swizzle string, every
option string, every use of a variable, etc. leaked memory.

Create a temporary buffer in the parser state (string_dumpster and
dumpster_size).  Return strings from the lexer to the parser in the
buffer.  Grow the buffer as needed.  When the parser needs to keep a
string (i.e., delcaring a new variable), let it make a copy then.

The only leak that valgrind now detects is /occasionally/ the copy of
the program string in gl_program::String is leaked.  I'm not seeing
how. :(
This commit is contained in:
Ian Romanick
2009-10-27 13:40:18 -07:00
parent 8df9587d68
commit 93dae6761b
5 changed files with 337 additions and 207 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -40,7 +40,7 @@
if (condition) { \
return token; \
} else { \
yylval->string = strdup(yytext); \
yylval->string = return_string(yyextra, yytext); \
return IDENTIFIER; \
} \
} while (0)
@@ -63,7 +63,7 @@
yylval->temp_inst.SaturateMode = SATURATE_ ## sat; \
return token; \
} else { \
yylval->string = strdup(yytext); \
yylval->string = return_string(yyextra, yytext); \
return IDENTIFIER; \
} \
} while (0)
@@ -71,6 +71,45 @@
#define SWIZZLE_INVAL MAKE_SWIZZLE4(SWIZZLE_NIL, SWIZZLE_NIL, \
SWIZZLE_NIL, SWIZZLE_NIL)
/**
* Send a string to the parser using asm_parser_state::string_dumpster
*
* Sends a string to the parser using asm_parser_state::string_dumpster as a
* temporary storage buffer. Data previously stored in
* asm_parser_state::string_dumpster will be lost. If
* asm_parser_state::string_dumpster is not large enough to hold the new
* string, the buffer size will be increased. The buffer size is \b never
* decreased.
*
* \param state Assembler parser state tracking
* \param str String to be passed to the parser
*
* \return
* A pointer to asm_parser_state::string_dumpster on success or \c NULL on
* failure. Currently the only failure case is \c ENOMEM.
*/
static char *
return_string(struct asm_parser_state *state, const char *str)
{
const size_t len = strlen(str);
if (len >= state->dumpster_size) {
char *const dumpster = _mesa_realloc(state->string_dumpster,
state->dumpster_size,
len + 1);
if (dumpster == NULL) {
return NULL;
}
state->string_dumpster = dumpster;
state->dumpster_size = len + 1;
}
memcpy(state->string_dumpster, str, len + 1);
return state->string_dumpster;
}
static unsigned
mask_from_char(char c)
{
@@ -308,7 +347,7 @@ ARRAYSHADOW1D { return_token_or_IDENTIFIER(require_ARB_fp && require
ARRAYSHADOW2D { return_token_or_IDENTIFIER(require_ARB_fp && require_shadow && require_texarray, TEX_ARRAYSHADOW2D); }
[_a-zA-Z$][_a-zA-Z0-9$]* {
yylval->string = strdup(yytext);
yylval->string = return_string(yyextra, yytext);
return IDENTIFIER;
}

View File

@@ -4565,7 +4565,7 @@ yyreduce:
"undefined variable binding in ALIAS statement");
YYERROR;
} else {
_mesa_symbol_table_add_symbol(state->st, 0, (yyvsp[(2) - (4)].string), target);
_mesa_symbol_table_add_symbol(state->st, 0, strdup((yyvsp[(2) - (4)].string)), target);
}
;}
break;
@@ -4896,10 +4896,14 @@ declare_variable(struct asm_parser_state *state, char *name, enum asm_type t,
if (exist != NULL) {
yyerror(locp, state, "redeclared identifier");
} else {
s = calloc(1, sizeof(struct asm_symbol));
s->name = name;
const size_t name_len = strlen(name);
s = calloc(1, sizeof(struct asm_symbol) + name_len + 1);
s->name = (char *)(s + 1);
s->type = t;
memcpy((char *) s->name, name, name_len + 1);
switch (t) {
case at_temp:
if (state->prog->NumTemporaries >= state->limits->MaxTemps) {
@@ -5147,6 +5151,11 @@ _mesa_parse_arb_program(GLcontext *ctx, GLenum target, const GLubyte *str,
_mesa_memcpy (strz, str, len);
strz[len] = '\0';
if (state->prog->String != NULL) {
_mesa_free(state->prog->String);
state->prog->String = NULL;
}
state->prog->String = strz;
state->st = _mesa_symbol_table_ctor();
@@ -5236,7 +5245,6 @@ error:
for (sym = state->sym; sym != NULL; sym = temp) {
temp = sym->next;
_mesa_free((void *) sym->name);
_mesa_free(sym);
}
state->sym = NULL;
@@ -5244,6 +5252,11 @@ error:
_mesa_symbol_table_dtor(state->st);
state->st = NULL;
if (state->string_dumpster != NULL) {
_mesa_free(state->string_dumpster);
state->dumpster_size = 0;
}
return result;
}

View File

@@ -1919,7 +1919,7 @@ ALIAS_statement: ALIAS IDENTIFIER '=' IDENTIFIER
"undefined variable binding in ALIAS statement");
YYERROR;
} else {
_mesa_symbol_table_add_symbol(state->st, 0, $2, target);
_mesa_symbol_table_add_symbol(state->st, 0, strdup($2), target);
}
}
;
@@ -2027,10 +2027,14 @@ declare_variable(struct asm_parser_state *state, char *name, enum asm_type t,
if (exist != NULL) {
yyerror(locp, state, "redeclared identifier");
} else {
s = calloc(1, sizeof(struct asm_symbol));
s->name = name;
const size_t name_len = strlen(name);
s = calloc(1, sizeof(struct asm_symbol) + name_len + 1);
s->name = (char *)(s + 1);
s->type = t;
memcpy((char *) s->name, name, name_len + 1);
switch (t) {
case at_temp:
if (state->prog->NumTemporaries >= state->limits->MaxTemps) {
@@ -2280,6 +2284,7 @@ _mesa_parse_arb_program(GLcontext *ctx, GLenum target, const GLubyte *str,
if (state->prog->String != NULL) {
_mesa_free(state->prog->String);
state->prog->String = NULL;
}
state->prog->String = strz;
@@ -2371,7 +2376,6 @@ error:
for (sym = state->sym; sym != NULL; sym = temp) {
temp = sym->next;
_mesa_free((void *) sym->name);
_mesa_free(sym);
}
state->sym = NULL;
@@ -2379,5 +2383,10 @@ error:
_mesa_symbol_table_dtor(state->st);
state->st = NULL;
if (state->string_dumpster != NULL) {
_mesa_free(state->string_dumpster);
state->dumpster_size = 0;
}
return result;
}

View File

@@ -38,6 +38,13 @@ enum asm_type {
at_output,
};
/**
* \note
* Objects of this type are allocated as the object plus the name of the
* symbol. That is, malloc(sizeof(struct asm_symbol) + strlen(name) + 1).
* Alternately, asm_symbol::name could be moved to the bottom of the structure
* and declared as 'char name[0];'.
*/
struct asm_symbol {
struct asm_symbol *next; /**< List linkage for freeing. */
const char *name;
@@ -157,6 +164,15 @@ struct asm_parser_state {
/*@}*/
/**
* Buffer to hold strings transfered from the lexer to the parser
*/
/*@{*/
char *string_dumpster; /**< String data transfered. */
size_t dumpster_size; /**< Total size, in bytes, of the buffer. */
/*@}*/
/**
* Selected limits copied from gl_constants
*