Hi, I've been experiencing a use-after-free with the current kconfig (from for-next, but I believe mainline is affected too), related to the introduction of commit 246cf9c26bf11f2bffbecea6e5bd222eee7b1df8. The symptoms with the attached test case and patch to highlight the problem is the following: % scripts/kconfig/mconf Kconfig.testcase [...] Invalid expr 0xbb9124a0 at scripts/kconfig/menu.c:295 in menu_finalize(): 41414141 zsh: abort (core dumped) scripts/kconfig/mconf Kconfig.testcase The backtrace is not really surprising: #2 0xbbb9fbd6 in abort () from /usr/lib/libc.so.12 #3 0x0804f23a in menu_finalize (parent=0xbb911e20) at scripts/kconfig/menu.c:295 #4 0x0804eb6d in menu_finalize (parent=0xbb911df0) at scripts/kconfig/menu.c:292 #5 0x0804f166 in menu_finalize (parent=0xbb911d60) at scripts/kconfig/menu.c:320 #6 0x0804eb6d in menu_finalize (parent=0xbb911c10) at scripts/kconfig/menu.c:292 #7 0x0804eb6d in menu_finalize (parent=0x8069ea0) at scripts/kconfig/menu.c:292 #8 0x080535d7 in conf_parse (name=0x804aa61 "Ç\004$") at scripts/kconfig/zconf.tab.c:2242 #9 0x0804aa61 in main (ac=Cannot access memory at address 0x0 (gdb) frame 3 #3 0x0804f23a in menu_finalize (parent=0xbb911e20) at scripts/kconfig/menu.c:295 295 EXPR_CHECK(parent->dir_dep); So far, I tracked this issue down to the faulty expression being freed by expr_eliminate_dups1() in the previous iteration of menu_initialize(), on the following line basedep = expr_eliminate_dups(basedep); The testcase is a stripped down Kconfig from the tree. - Arnaud
Attachment:
Kconfig.testcase
Description: Binary data
Attachment:
Kconfig.testcase.l1
Description: Binary data
diff --git a/Kconfig.testcase b/Kconfig.testcase new file mode 100644 index 0000000..cb663e9 --- /dev/null +++ b/Kconfig.testcase @@ -0,0 +1 @@ +source "Kconfig.testcase.l1" diff --git a/Kconfig.testcase.l1 b/Kconfig.testcase.l1 new file mode 100644 index 0000000..79ec19e --- /dev/null +++ b/Kconfig.testcase.l1 @@ -0,0 +1,216 @@ +config DUMMY_BOOL_0 + bool + +config DUMMY_BOOL_1 + bool + +config DUMMY_BOOL_2 + bool + +config DUMMY_BOOL_3 + bool + default y + +config DUMMY_BOOL_4 + bool + default (DUMMY_COND_5 || DUMMY_COND_6) + +config DUMMY_STRING_0 + string + default "string0" if DUMMY_COND_7 + default "string1" if DUMMY_COND_8 + +config DUMMY_BOOL_5 # maybe unneeded + bool + default y + +config DUMMY_BOOL_6 + bool + default y + +config DUMMY_BOOL_7 + bool + default y + select DUMMY_SELECT_0 if DUMMY_COND_0 + select DUMMY_SELECT_1 + select DUMMY_SELECT_2 + select DUMMY_SELECT_3 + select DUMMY_SELECT_4 + +config DUMMY_BOOL_8 + bool + default y + depends on DUMMY_COND_9 || (DUMMY_COND_10 && DUMMY_COND_11) + +config DUMMY_BOOL_9 + bool + default y + select DUMMY_SELECT_5 + select DUMMY_SELECT_6 + select DUMMY_SELECT_7 if (!DUMMY_COND_1 || !DUMMY_COND_2) + select DUMMY_SELECT_8 + select DUMMY_SELECT_9 + +config DUMMY_BOOL_10 + bool + default y + select DUMMY_SELECT_10 + select DUMMY_SELECT_11 + select DUMMY_SELECT_12 + select DUMMY_SELECT_13 + select DUMMY_SELECT_14 + +config DUMMY_BOOL_11 + bool + default y + select DUMMY_SELECT_15 + select DUMMY_SELECT_16 + select DUMMY_SELECT_17 + select DUMMY_SELECT_18 + select DUMMY_SELECT_19 + +config DUMMY_BOOL_12 + bool + default y + select DUMMY_SELECT_20 if DUMMY_COND_3 + select DUMMY_SELECT_21 + select DUMMY_SELECT_22 + select DUMMY_SELECT_23 + select DUMMY_SELECT_24 + +config DUMMY_BOOL_13 + bool + default (DUMMY_COND_12 || DUMMY_COND_13 || DUMMY_COND_14) + select DUMMY_SELECT_25 + select DUMMY_SELECT_26 if DUMMY_COND_4 + select DUMMY_SELECT_27 + select DUMMY_SELECT_28 + select DUMMY_SELECT_29 + +config DUMMY_BOOL_14 + bool + default y + depends on DUMMY_COND_15 + select DUMMY_SELECT_30 + select DUMMY_SELECT_31 + select DUMMY_SELECT_32 + select DUMMY_SELECT_33 + select DUMMY_SELECT_34 if DUMMY_COND_16 + +config DUMMY_BOOL_16 + bool + default y + +config DUMMY_BOOL_17 + bool + default y + +config DUMMY_BOOL_18 + bool + default y + +config DUMMY_BOOL_19 + bool + default y + +config DUMMY_BOOL_20 + bool + default y + +config DUMMY_BOOL_21 + bool + default y + +config DUMMY_BOOL_22 + bool + default y + +config DUMMY_BOOL_23 + bool + default y + +config DUMMY_BOOL_24 + bool + default y + +config DUMMY_BOOL_30 + bool + default y + +config DUMMY_BOOL_25 + bool + default y + +config DUMMY_BOOL_26 + bool + default y + +config DUMMY_BOOL_27 + bool + default y + +config DUMMY_BOOL_28 + bool + default y + +config DUMMY_BOOL_29 + bool + default y + +config DUMMY_BOOL_30 + bool + default y + depends on DUMMY_COND_17 && DUMMY_COND_18 && DUMMY_COND_19 + +config DUMMY_STRING_1 + string + option env="ENV0" + +config DUMMY_STRING_2 + string + option env="ENV1" + +config DUMMY_STRING_3 + string + depends on !UML + option defconfig_list + default "string2" + default "string3" + default "string4" + default "_string5" + default "string6" + +menu "dummy menu 0" + +config DUMMY_BOOL_31 + bool "prompt0" + +config DUMMY_BOOL_32 + bool + +menuconfig DUMMY_BOOL_33 + bool "prompt1" + depends on DUMMY_COND_20 + +if DUMMY_BOOL_33 + +config DUMMY_BOOL_34 + bool "prompt2" + depends on DUMMY_BOOL_33 + +config DUMMY_BOOL_35 + bool "prompt3" + depends on DUMMY_COND_21 + +config DUMMY_BOOL_36 + bool "prompt4" + depends on DUMMY_COND_22 && DUMMY_COND_23 + +menuconfig DUMMY_BOOL_37 + bool "prompt5" + depends on DUMMY_COND_24 + +endif + +endmenu + diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index 330e7c0..65f7ab3 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -71,6 +71,8 @@ struct expr *expr_copy(struct expr *org) if (!org) return NULL; + EXPR_CHECK(org); + e = malloc(sizeof(*org)); memcpy(e, org, sizeof(*org)); switch (org->type) { @@ -101,11 +103,13 @@ struct expr *expr_copy(struct expr *org) return e; } -void expr_free(struct expr *e) +void _expr_free(struct expr *e) { if (!e) return; + EXPR_CHECK(e); + switch (e->type) { case E_SYMBOL: break; @@ -124,6 +128,9 @@ void expr_free(struct expr *e) printf("how to free type %d?\n", e->type); break; } + + memset(e, 'A', sizeof(e)); + free(e); } diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 6ee2e4f..0adc104 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -48,6 +48,26 @@ struct expr { #define EXPR_AND(dep1, dep2) (((dep1)<(dep2))?(dep1):(dep2)) #define EXPR_NOT(dep) (2-(dep)) +#if 1 +#define expr_free(e) \ +({ \ + fprintf(stderr, "%s: %p:%d\n", __func__, e, __LINE__); \ + _expr_free(e); \ + e = NULL; \ +}) +#else +#define expr_free(e) _expr_free(e); +#endif + +#define EXPR_CHECK(e) \ +({ \ + if ((e) != NULL && ((e)->type < E_NONE || (e)->type > E_RANGE)) { \ + fprintf(stderr, "Invalid expr %p at %s:%d in %s(): %x\n", \ + e, __FILE__, __LINE__, __func__, (e)->type); \ + abort(); \ + } \ +}) + #define expr_list_for_each_sym(l, e, s) \ for (e = (l); e && (s = e->right.sym); e = e->left.expr) @@ -193,7 +213,7 @@ struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symb struct expr *expr_alloc_and(struct expr *e1, struct expr *e2); struct expr *expr_alloc_or(struct expr *e1, struct expr *e2); struct expr *expr_copy(struct expr *org); -void expr_free(struct expr *e); +void _expr_free(struct expr *e); int expr_eq(struct expr *e1, struct expr *e2); void expr_eliminate_eq(struct expr **ep1, struct expr **ep2); tristate expr_calc_value(struct expr *e); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 4fb5902..3563239 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -292,6 +292,7 @@ void menu_finalize(struct menu *parent) menu_finalize(menu); } else if (sym) { /* ignore inherited dependencies for dir_dep */ + EXPR_CHECK(parent->dir_dep); sym->dir_dep.expr = expr_transform(expr_copy(parent->dir_dep)); sym->dir_dep.expr = expr_eliminate_dups(sym->dir_dep.expr);