Stale expression reference causing use-after-free

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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);
 

[Index of Archives]     [Linux&nblp;USB Development]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite Secrets]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux