google / silifuzz

Apache License 2.0
380 stars 25 forks source link

False positive eflags SDC on intel 12th gen CPUs #2

Open Maknee opened 1 year ago

Maknee commented 1 year ago

Hi, silifuzz authors

I recently found many false positives with dealing with eflags on the two 12900 CPUs I'm fuzzing on. I'm not sure if this would affect other 12th gen intel CPUs.

Example shown below:

Bytes: \xBC\xC4\x04\x8A\xD2\x66\x81\xDE\xF2\x00\x69\x9F\xC5\xAA\x18\x1D\x5D\x20\xE1\x00

Instructions:

0x85c2d36000:   mov     esp, 0xd28a04c4                                                              
0x85c2d36005:   sbb     si, 0xf2                                                                     
0x85c2d3600a:   imul    ebx, dword ptr [rdi + 0x1d18aac5], 0xe1205d     

Output from running corpus

./runner/reading_runner_main_nolibc extracted_snapshot.corpus --cpu=0                                                                    
E<DATE> <PID> runner.cc:439] Snapshot [stdin] failed, outcome = 3                                                                                                                                          
I<DATE> <PID> runner.cc:441] Registers (diff vs expected end_state 0):                                                                                                                                     
I<DATE> <PID> runner.cc:442]   gregs (modified only):
I<DATE> <PID> logging_util.cc:25]   eflags = 0x206 want 0x246
I<DATE> <PID> runner.cc:451]   fpregs (modified only): 

In this case, the corpus expects eflags to have ZF (Zero flag) set, but the flag is set depending on which core the set of instructions runs on. On a 12900 cores 0-15 are p cores while cores 16-23 are e-cores

The instruction that influences the zero flag in this scenario is the imul ebx, dword ptr [rdi + 0x1d18aac5], 0xe1205d instruction.

The GDB dump below represents state of the program after the execution of the instructions.

Running on an e-core produces the expected result, the zero flag is set. (Running this on cores 16-23)

> 0x00000085c2d36014:  ff 15 00 00 00 00       call   QWORD PTR [rip+0x0]        # 0x85c2d3601a
rax            0x20000000          536870912
rbx            0x0                 0
rcx            0x100               256
rdx            0x20000000          536870912
rsi            0x2000ff0e          536936206
rdi            0x20000000          536870912
rbp            0x20000000          0x20000000
rsp            0xd28a04c4          0xd28a04c4
r8             0x20000000          536870912
r9             0x20000000          536870912
r10            0x20000000          536870912
r11            0x20000000          536870912
r12            0x20000000          536870912
r13            0x20000000          536870912
r14            0x20000000          536870912
r15            0x20000000          536870912
rip            0x85c2d36014        0x85c2d36014
eflags         0x246               [ PF ZF IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0

However, running on an p-core produces the incorrect result, the zero flag is not set. (Running this on cores 0-15)

=> 0x00000085c2d36014:  ff 15 00 00 00 00       call   QWORD PTR [rip+0x0]        # 0x85c2d3601a
rax            0x20000000          536870912
rbx            0x0                 0
rcx            0x100               256
rdx            0x20000000          536870912
rsi            0x2000ff0e          536936206
rdi            0x20000000          536870912
rbp            0x20000000          0x20000000
rsp            0xd28a04c4          0xd28a04c4
r8             0x20000000          536870912
r9             0x20000000          536870912
r10            0x20000000          536870912
r11            0x20000000          536870912
r12            0x20000000          536870912
r13            0x20000000          536870912
r14            0x20000000          536870912
r15            0x20000000          536870912
rip            0x85c2d36014        0x85c2d36014
eflags         0x206               [ PF IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0

According to the intel manual Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 2 Page 3-490, it states that for the imul instruction "The SF, ZF, AF, and PF flags are undefined."

Thus, I believe that this is a false positive because the ZF should not matter in this scenario (whether it is set or not) for the expected output.

Is there a way to filter out this false positive case that silifuzz produces?

ksteuck commented 1 year ago

Hi @Maknee,

First of all, thanks for reporting this and for the excellent analysis. This is indeed a false-positive. We have not extensively tested SiliFuzz outside of the platforms mentioned in the README and it's a bummer we didn't catch this earlier. In terms of workarounds, the most immediate thing you can do is taskset(1) the entire pipeline (simple_fix_tool + ochestrator/runner) such that it runs on just E-/P- cores. Effectively this means producing two sets of corpus files. This is similar to the existing PlatformId except there's no native support for this.

Filtering this out is hard-to-impossible I would say. A naive approach of simply not comparing the EFLAGS fails if you consider conditional branching and PUSHF. We can ostensibly utilize the existing single-stepping machinery here but that would require good knowledge of the ISA

ksteuck commented 1 year ago

I'm not sure if this would affect other 12th gen intel CPUs.

I suspect all of the processors listed here https://en.wikipedia.org/wiki/Alder_Lake#List_of_12th_generation_Alder_Lake_processors are affected if they have both P- and E-cores. I don't have immediate access to any of these to verify.

Maknee commented 1 year ago

Thanks @ksteuck for confirming that this case is a false positive.

The first suggestion should work -- producing two different set of corpuses to run on p-cores/e-cores. The second suggestion is definitely more tricky to get right. In some cases, eflag comparisons do matter as you mentioned.

Another idea I was thinking of is a tool that can inject some value (say 0) for values that are undefined.

Say, we're trying to deal with the false positive I mentioned.

This tool can scan for certain instructions (say imul instruction) and inject a sequence of instructions to set the ZF to 0 without affecting other flags after executing the imul instruction.

For example, consider this sequence to zero out the zero flag.

pushfd
and dword ptr [rsp], 0xFFFFFFCF
popfd

Another way to handle this case is to attach a debugger to the runner, set a breakpoint and modify ZF when the instruction is hit and the resume execution afterwards. This will incur much more overhead for fuzzing.

However, the ideas I mentioned require a bit of engineering and manual work to generate sequences for instructions that produce undefined results in certain registers. I think it's simplest and quickest to run different set of corpuses on different set of cores and I'll probably do that.

Hopefully, this type of false positive only occurs on 12th gen processors. I don't have access to other types of processors with p/e cores to check if this false positive case occurs on those processors.

ksteuck commented 1 year ago

This tool can scan for certain instructions (say imul instruction) and inject a sequence of instructions to set the ZF to 0 without affecting other flags after executing the imul instruction.

Injecting code like this opens an entirely new can of worms unfortunately and introduces an undesirable skew between the corpus generation and corpus execution parts of the pipeline.

As long as the undefined bits are consistent within a single microarch it makes more sense for us to keep the current approach. This means modelling a single Alderlake CPU as two separate CPUs and all the added complexity associated with it.