Symcc fails to generate new inputs when there is a xor operation
ret2dir opened this issue · comments
Dear developers,
I've learned that Symcc will instrument function _sym_push_path_constraint
before each branch to capture the concrete execution state and further negate current constraints to generate new inputs. The second parameter taken
of function _sym_push_path_constraint
records the result of the current condition statement, which is assumed to be 1 or 0 (which corresponds to true or false).
However, I found that for the following C program, the second parameter taken
has a value of 255 when the first condition holds (where g_927 is 0) and 254 when not (where g_927 is not 0). In that case, if the initial value of g_927 is 0 as the code shows, symcc will treat the first condition always holds, negates current constraint, finally still generates inputs that g_927 is 0, which fails to explore another path where g_927 is not 0.
#include <unistd.h>
#include <stdint.h>
struct S0 {
uint16_t f0;
};
static int8_t g_2 = 0;
static uint16_t g_927 = 0;
static int32_t func_1() {
int32_t l_968 = 6;
if (l_968 = !(0 == g_927)) {
struct S0 l_974, l_1047;
if (9 && (l_974.f0 = g_2 > 0 && l_1047.f0) == l_974.f0 <= 1);
}
}
void main () {
read(STDIN_FILENO, &g_2, sizeof(g_2));
read(STDIN_FILENO, &g_927, sizeof(g_927));
func_1();
}
I have checked the reason by reading the LLVM IR code after instrumentation and machine code of instrumented executable. The below code shows there is an extra xor
operation before invoking _sym_push_path_constraint
. The compilation process transfers true
to 0xFF
in machine code, which causes the above problem.
%17 = icmp eq i32 0, %11
...
%22 = xor i1 %17, true
...
call void @_sym_push_path_constraint(i8* %21, i1 %22, i64 33447272)
Even though the value of %22
is 255 or 254, it will not affect the concrete execute, due to the following test
opcode before jnz
. Therefore, I suppose that this problem can be solved if we perform & 0x1
operation on parameter taken
before using it to negate the current constraint and generate new inputs.
void _sym_push_path_constraint(Z3_ast constraint, int taken,
uintptr_t site_id [[maybe_unused]]) {
taken = taken & 0x1; // <==== newly added
if (constraint == nullptr)
return;
...
}
Hope it works. Thank you a lot.
Thanks for the detailed report. Is the problem only when using the simple backend? simple_backend/Runtime.cpp
Is the problem also present with the QSym backend ?
Unfortunately, the example code you provided is not actually taking any symbolic input (no stdin) which makes the issue difficult to reproduce...
Would you be able to provide a PR, with a test case demonstrating the problem:
- that integrates in the test suite, see for example test/if.c and
- a patch to fix it?
That would be very helpful!
Thanks for the prompt reply. I've tried this test case on both simple backend and qsym backend, all of them have this problem.
Sorry for providing an improper test case without symbolic input. I revised the code with stdin in the previous post. Both the test case and the input that can reproduce the bug are placed in https://bitbucket.org/ret2dir/testcase/src/master/.
You can easily reproduce the bug:
- compile testcase.c with symcc
symcc testcase.c
(simple backend or qsym backend) - execute
mkdir output; cat test0.input | SYMCC_OUTPUT_DIR=output ./a.out
The input provides input g_927=0
and g_2=0
, however, from the output, you can find that the newly generated input is still g_927=0
, which means symcc fails to generate new inputs. Hope it works.
I will provide a PR sooner. Thank you.