The following code causes a segfault
void
foo(int n)
{
return;
switch (n) {
case 0:
break;
}
}
$ cproc -o test.o -c test.c
cproc: compile: process signaled: Segmentation fault
cproc: codegen: process 24579 exited with status 1
cproc: assemble: process signaled: Terminated
$ valgrind --trace-children=yes cproc -o test.o -c test.c
...
==24591== Invalid read of size 4
==24591== at 0x11E12F: emitvalue (qbe.c:1031)
==24591== by 0x11E720: emitinst (qbe.c:1139)
==24591== by 0x11ED14: emitfunc (qbe.c:1254)
==24591== by 0x10E2FF: decl (decl.c:1001)
==24591== by 0x1144BD: main (main.c:60)
==24591== Address 0x0 is not stack'd, malloc'd or (recently) free'd
The code where I encountered this had the
return;
wrapped in a#ifdef
so that the function became a noop under certain conditions.
This is a long-standing issue that stems from
funcinst
returningNULL
when we haven't introduced a new QBE block, and we have already have a jump for the current block. The intent was to avoid generating unreachable code, but it causes problems when more control flow follows after the jump.One fix could be to make
stmt()
split up parsing and code generation, and skip the latter if the current block is already terminated. However, the much simpler solution is just to insert a dummy block when we encounter this situation. QBE will discard unreachable blocks quite early in the pipeline, so I think that's what I'll do.Fixed by 4fa48e716d6381e51ae8dc7a9992c4c7d50219a1.