Hello, I've pushed a couple miniscule commits to mob and would like to work up to more complex tasks. I've reported more issues than I've fixed and would like to turn that around 😀
I'm two-thirds of the way debugging this NULL pointer dereference in *cur_scope I found fuzzing with afl++. The minimal test case is n[sizeof({ It may not crash outright unless you use -fstack-protector (ProPolice), which is enabled on Debian's package for example. Bisecting (0.9.27 was in the clear) I found this is the responsible commit: commit 4a70b2bc2d080baa4082d9d902f9172f88021019 Author: Michael Matz <m...@suse.de> Date: Wed Jan 15 23:32:40 2020 +0100 Fix handling of unevaluated subexpression of const we were emitting error messages for something like 'static int i = 2 || 1/0', even though the exception would be in the unevaluated part. This doesn't destroy const-ness, so we must accept it. This requires splitting the nocode_wanted values a bit more, so that nocode_wanted due to const_wanted can be differentiated from nocode_wanted due to non-evaluation. tccgen.c | 9 +++++---- Here's a portion of the non-test-suite changes that are pertinent: @@ -53,6 +53,7 @@ static SValue _vstack[1 + VSTACK_SIZE]; ST_DATA int nocode_wanted; /* no code generation wanted */ +#define unevalmask 0xffff /* unevaluated subexpression */ #define NODATA_WANTED (nocode_wanted > 0) /* no static data output wanted either */ #define STATIC_DATA_WANTED (nocode_wanted & 0xC0000000) /* only static data output */ @@ -5104,7 +5105,7 @@ ST_FUNC void unary(void) } } else if (tok == '{') { int saved_nocode_wanted = nocode_wanted; - if (const_wanted) + if (const_wanted && !(nocode_wanted & unevalmask)) tcc_error("expected constant"); /* save all registers */ save_regs(0); @@ -6152,9 +6153,9 @@ ST_FUNC void gexpr(void) static void expr_const1(void) { const_wanted++; - nocode_wanted++; + nocode_wanted += unevalmask + 1; expr_cond(); - nocode_wanted--; + nocode_wanted -= unevalmask + 1; Notice how the change in unary() pertains to the '{' character. Commenting out the '&& !(nocode_wanted & unevalmask)' check is sufficient to fix this, but surely it was put there for a reason. Anyway, unary() doesn't stop the bad input right away and hands the input off to block(): } else if (t == '{') { struct scope o; new_scope(&o); The NULL pointer dereference happens in new_scope() right here (I put the following assertion myself which fails even without ProPolice) void new_scope(struct scope *o) { /* copy and link previous scope */ assert(cur_scope != NULL); *o = *cur_scope; Here's a backtrace excerpt: #5 new_scope (o=<optimized out>) at tccgen.c:6425 #6 0x000055555556affa in block (is_expr=is_expr@entry=1) at tccgen.c:6538 #7 0x000055555556ccae in unary () at tccgen.c:5117 I think most variable names aren't very descriptive however and `bt full` looks like alphabet soup, so I guess cur_scope is probably uninitialized but this is as far as I've got. If someone could nudge me along or feels like finishing the job, either would be great. Sincerely, John
signature.asc
Description: This is a digitally signed message part.
_______________________________________________ Tinycc-devel mailing list Tinycc-devel@nongnu.org https://lists.nongnu.org/mailman/listinfo/tinycc-devel