When a symbol that is already registered is read again from *.symref file, __add_symbol() removes the previous one from the hash table without freeing it. [Test Case] $ cat foo.c #include <linux/export.h> void foo(void); void foo(void) {} EXPORT_SYMBOL(foo); $ cat foo.symref foo void foo ( void ) foo void foo ( void ) When a symbol is removed from the hash table, it must be freed along with its ->name and ->defn members. However, sym->name cannot be freed because it is sometimes shared with node->string, but not always. If sym->name and node->string share the same memory, free(sym->name) could lead to a double-free bug. To resolve this issue, always assign a strdup'ed string to sym->name.
Unified · Split
| @@ -272,11 +272,15 @@ static struct symbol *__add_symbol(const char *name, enum symbol_type type, · 271 unmodified lines | |||
| 272 | break; | 272 | break; |
| 273 | } | 273 | } |
| 274 | } | 274 | } |
| 275 | |||
| 276 | free_list(sym->defn, NULL); | ||
| 277 | free(sym->name); | ||
| 278 | free(sym); | ||
| 275 | --nsyms; | 279 | --nsyms; |
| 276 | } | 280 | } |
| 277 | 281 | ||
| 278 | sym = xmalloc(sizeof(*sym)); | 282 | sym = xmalloc(sizeof(*sym)); |
| 279 | sym->name = name; | 283 | sym->name = xstrdup(name); |
| 280 | sym->type = type; | 284 | sym->type = type; |
| 281 | sym->defn = defn; | 285 | sym->defn = defn; |
| 282 | sym->expansion_trail = NULL; | 286 | sym->expansion_trail = NULL; |
| @@ -483,7 +487,7 @@ static void read_reference(FILE *f) · 200 unmodified lines | |||
| 483 | defn = def; | 487 | defn = def; |
| 484 | def = read_node(f); | 488 | def = read_node(f); |
| 485 | } | 489 | } |
| 486 | subsym = add_reference_symbol(xstrdup(sym->string), sym->tag, | 490 | subsym = add_reference_symbol(sym->string, sym->tag, |
| 487 | defn, is_extern); | 491 | defn, is_extern); |
| 488 | subsym->is_override = is_override; | 492 | subsym->is_override = is_override; |
| 489 | free_node(sym); | 493 | free_node(sym); |
| @@ -32,7 +32,7 @@ struct string_list { · 31 unmodified lines | |||
| 32 | 32 | ||
| 33 | struct symbol { | 33 | struct symbol { |
| 34 | struct symbol *hash_next; | 34 | struct symbol *hash_next; |
| 35 | const char *name; | 35 | char *name; |
| 36 | enum symbol_type type; | 36 | enum symbol_type type; |
| 37 | struct string_list *defn; | 37 | struct string_list *defn; |
| 38 | struct symbol *expansion_trail; | 38 | struct symbol *expansion_trail; |
| @@ -482,12 +482,12 @@ enumerator_list: · 481 unmodified lines | |||
| 482 | enumerator: | 482 | enumerator: |
| 483 | IDENT | 483 | IDENT |
| 484 | { | 484 | { |
| 485 | const char *name = strdup((*$1)->string); | 485 | const char *name = (*$1)->string; |
| 486 | add_symbol(name, SYM_ENUM_CONST, NULL, 0); | 486 | add_symbol(name, SYM_ENUM_CONST, NULL, 0); |
| 487 | } | 487 | } |
| 488 | | IDENT '=' EXPRESSION_PHRASE | 488 | | IDENT '=' EXPRESSION_PHRASE |
| 489 | { | 489 | { |
| 490 | const char *name = strdup((*$1)->string); | 490 | const char *name = (*$1)->string; |
| 491 | struct string_list *expr = copy_list_range(*$3, *$2); | 491 | struct string_list *expr = copy_list_range(*$3, *$2); |
| 492 | add_symbol(name, SYM_ENUM_CONST, expr, 0); | 492 | add_symbol(name, SYM_ENUM_CONST, expr, 0); |
| 493 | } | 493 | } |