IntelLabs / IntelLabs/kAFL

Crash exec_result_code can be overwritten by the normal sync page_not_found path before host samling, causing real crashes to be reported as regular

Open
#345 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Makefile
Stars
814
Forks
109
PR merge metrics
No merged PRs in 30d

Description

Hi,

I observed a real crash misclassification in `kafl.qemu` that appears to come from the upstream result handoff logic.

This is not only a source-level concern: I instrumented the QEMU/Nyx paths locally and directly observed a real `rc_crash` result being overwritten with `rc_success` before the host fuzzer sampled it.

## Observed behavior

A target reliably triggered a real panic/crash path, but the input was sometimes reported to the fuzzer as `regular` instead of `crash`.

After adding tracing around the aux-buffer result flow, I observed this sequence:

1. panic path entered
2. `exec_result_code` set to `rc_crash`
3. reload completed
4. before the next host-visible sync point, `exec_result_code` was overwritten from `rc_crash` to `rc_success`
5. the Python worker then sampled `success` and classified the input as `regular`

## Directly observed overwrite path

The overwrite I observed was:

- terminal result written in the panic/crash path
- then overwritten in the next normal sync path by `page_not_found` handling

In other words, this was not a hypothetical overwrite candidate.
I directly observed:

- `crash -> success`
- at the `synchronization_lock()/page_not_found` path
- before host sampling

## Why this happens

### 1. Host sampling is delayed

In `kafl.fuzzer`, the host does not continuously observe the aux buffer.
It only reads the result when QEMU reaches the next control-socket sync boundary.

Relevant logic is in:
- `kafl_fuzzer/worker/qemu.py`
- `run_qemu()`
- `send_payload()`

So once a terminal result is written, it must remain stable until the next host-visible sync point.

### 2. Crash path writes the result before host sampling

In upstream `kafl.qemu`:

- `handle_hypercall_kafl_panic()`
- `synchronization_lock_crash_found()`

The panic path writes `rc_crash`, performs reload, and returns.
The host does not sample the aux buffer at that exact point.

### 3. The aux result uses a single mutable `exec_result_code` slot

Multiple paths write directly into the same `exec_result_code` byte, including:

- `set_crash_auxiliary_result_buffer()`
- `set_timeout_auxiliary_result_buffer()`
- `set_asan_auxiliary_result_buffer()`
- `set_success_auxiliary_result_buffer()`

So the crash result is not latched.

### 4. The next normal sync path can overwrite it

In the run I traced, the overwrite happened in:

- `synchronization_lock()`
- specifically the `synchronization_check_page_not_found()` / `page_not_found` handling path

That path wrote `success` before the host sampled the earlier crash result.

## Why I think this is an upstream bug

The implementation relies on the assumption that once a terminal result is written, it will still be present when the host reads it later.

But in the current structure:

- terminal result is written first
- host sampling happens later
- normal sync code can still write into the same result slot

That makes the crash-report handoff non-atomic with respect to host observation.

## Expected behavior

If the current execution has already been classified as `crash`, the host should not later observe that same execution as `regular`.

Once `exec_result_code` becomes terminal for an execution, later non-terminal sync logic should not downgrade it before host sampling.

## Suggested fix direction

A robust fix would be:

1. give terminal results (`crash`, `timeout`, `asan`, etc.) a dedicated handoff path,
2. record `exec_done` / runtime / dirty-page metadata there,
3. perform the host `PING`/wait handshake immediately for that terminal result,
4. only reset the result slot after host ACK,
5. prevent normal sync paths from overwriting an already-terminal `exec_result_code`.

## Practical impact

This can cause real crashes to be misclassified as `regular`, depending on the post-reload sync flow.

If useful, I can also provide a minimal patch that fixes this by preserving terminal results until host handoff and guarding the normal sync overwrite path.

Thank you for reading ! :)

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.