eurecom-s3 / symcc

SymCC: efficient compiler-based symbolic execution

Home Page:http://www.s3.eurecom.fr/tools/symbolic_execution/symcc.html

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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)

4ae147737d831f62a3493ea1a5e8e2c

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:

  1. that integrates in the test suite, see for example test/if.c and
  2. 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:

  1. compile testcase.c with symcc symcc testcase.c (simple backend or qsym backend)
  2. 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.