diff options
| author | Michael Matz <matz@suse.de> | 2016-08-15 05:09:31 +0200 |
|---|---|---|
| committer | Michael Matz <matz@suse.de> | 2016-12-15 17:47:10 +0100 |
| commit | be6d8ffc107db78d0542b5ee2d3306e7e132725c (patch) | |
| tree | fd76a1e61b710ed0cec5b5d800a3be4868be9b72 /tests | |
| parent | d0d25ec7df636deaf650d31ae3b50e60659f54dd (diff) | |
| download | tinycc-be6d8ffc107db78d0542b5ee2d3306e7e132725c.tar.gz tinycc-be6d8ffc107db78d0542b5ee2d3306e7e132725c.tar.bz2 | |
Fix access-after-free with statement expressions
The return value of statement expressions might refer to local
symbols, so those can't be popped. The old error message always
was just a band-aid, and since disabling it for pointer types it
wasn't effective anyway. It also never considered that also the
vtop->sym member might have referred to such symbols (see the
testcase with the local static, that used to segfault).
For fixing this (can be seen better with valgrind and SYM_DEBUG)
simply leave local symbols of stmt exprs on the stack.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/tcctest.c | 46 |
1 files changed, 46 insertions, 0 deletions
diff --git a/tests/tcctest.c b/tests/tcctest.c index 2abbacd..7ef63b8 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -2446,10 +2446,17 @@ void typeof_test(void) printf("a=%f b=%f c=%f\n", a, b, c); } + +struct hlist_node; +struct hlist_head { + struct hlist_node *first, *last; +}; + void statement_expr_test(void) { int a, i; + /* Basic stmt expr test */ a = 0; for(i=0;i<10;i++) { a += 1 + @@ -2461,6 +2468,45 @@ void statement_expr_test(void) } printf("a=%d\n", a); + /* Test that symbols aren't freed prematurely. + With SYM_DEBUG valgrind will show a read from a freed + symbol, and tcc will show an (invalid) warning on the initialization + of 'ptr' below, if symbols are popped after the stmt expr. */ + void *v = (void*)39; + typeof(({ + (struct hlist_node *)v; + })) x; + typeof (x) + ptr = (struct hlist_node *)v; + + /* This part used to segfault when symbols were popped prematurely. + The symbols for the static local would be overwritten with + helper symbols from the pre-processor expansions in between. */ +#define some_attr __attribute__((aligned(1))) +#define tps(str) ({ \ + static const char *t some_attr = str; \ + t; \ + }) + printf ("stmtexpr: %s %s\n", + tps("somerandomlongstring"), + tps("anotherlongstring")); + + /* Test that the three decls of 't' don't interact. */ + int t = 40; + int b = ({ int t = 41; t; }); + int c = ({ int t = 42; t; }); + + /* Test that aggregate return values work. */ + struct hlist_head h + = ({ + typedef struct hlist_head T; + long pre = 48; + T t = { (void*)43, (void*)44 }; + long post = 49; + t; + }); + printf ("stmtexpr: %d %d %d\n", t, b, c); + printf ("stmtexpr: %ld %ld\n", (long)h.first, (long)h.last); } void local_label_test(void) |
