| From 8d674b8609b246bba14b9035fc1ac45bb59af68f Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 3 Jan 2025 16:30:39 +0900 |
| Subject: genksyms: fix memory leak when the same symbol is read from *.symref |
| file |
| |
| From: Masahiro Yamada <masahiroy@kernel.org> |
| |
| [ Upstream commit be2fa44b5180a1f021efb40c55fdf63c249c3209 ] |
| |
| 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. |
| |
| Fixes: 64e6c1e12372 ("genksyms: track symbol checksum changes") |
| Signed-off-by: Masahiro Yamada <masahiroy@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| scripts/genksyms/genksyms.c | 8 ++++++-- |
| scripts/genksyms/genksyms.h | 2 +- |
| scripts/genksyms/parse.y | 4 ++-- |
| 3 files changed, 9 insertions(+), 5 deletions(-) |
| |
| diff --git a/scripts/genksyms/genksyms.c b/scripts/genksyms/genksyms.c |
| index 6ddc8f406c75f..6b0eb3898e4ec 100644 |
| --- a/scripts/genksyms/genksyms.c |
| +++ b/scripts/genksyms/genksyms.c |
| @@ -274,11 +274,15 @@ static struct symbol *__add_symbol(const char *name, enum symbol_type type, |
| break; |
| } |
| } |
| + |
| + free_list(sym->defn, NULL); |
| + free(sym->name); |
| + free(sym); |
| --nsyms; |
| } |
| |
| sym = xmalloc(sizeof(*sym)); |
| - sym->name = name; |
| + sym->name = xstrdup(name); |
| sym->type = type; |
| sym->defn = defn; |
| sym->expansion_trail = NULL; |
| @@ -485,7 +489,7 @@ static void read_reference(FILE *f) |
| defn = def; |
| def = read_node(f); |
| } |
| - subsym = add_reference_symbol(xstrdup(sym->string), sym->tag, |
| + subsym = add_reference_symbol(sym->string, sym->tag, |
| defn, is_extern); |
| subsym->is_override = is_override; |
| free_node(sym); |
| diff --git a/scripts/genksyms/genksyms.h b/scripts/genksyms/genksyms.h |
| index 21ed2ec2d98ca..5621533dcb8e4 100644 |
| --- a/scripts/genksyms/genksyms.h |
| +++ b/scripts/genksyms/genksyms.h |
| @@ -32,7 +32,7 @@ struct string_list { |
| |
| struct symbol { |
| struct symbol *hash_next; |
| - const char *name; |
| + char *name; |
| enum symbol_type type; |
| struct string_list *defn; |
| struct symbol *expansion_trail; |
| diff --git a/scripts/genksyms/parse.y b/scripts/genksyms/parse.y |
| index 840371d01bf48..689cb6bb40b65 100644 |
| --- a/scripts/genksyms/parse.y |
| +++ b/scripts/genksyms/parse.y |
| @@ -482,12 +482,12 @@ enumerator_list: |
| enumerator: |
| IDENT |
| { |
| - const char *name = strdup((*$1)->string); |
| + const char *name = (*$1)->string; |
| add_symbol(name, SYM_ENUM_CONST, NULL, 0); |
| } |
| | IDENT '=' EXPRESSION_PHRASE |
| { |
| - const char *name = strdup((*$1)->string); |
| + const char *name = (*$1)->string; |
| struct string_list *expr = copy_list_range(*$3, *$2); |
| add_symbol(name, SYM_ENUM_CONST, expr, 0); |
| } |
| -- |
| 2.39.5 |
| |