Open RongxiYe opened 1 week ago
Hi, I already know that id is generated through libafl_qemu_hook_edge_gen
->create_gen_wrapper->gen_hashed_edge_ids
(in StdEdgeCoverageChildModule
). Now I am debugging this part of code...
I found the process of calculating id and the intermediate value. The calculated id is indeed 0x4d5543f7dbc53456. Do you think there is a problem?
// src is 0x40a23030, dest is 0x40a23058
*RAX 0x4a265dc83567e8c8 hash_me(src)
*RAX 0x7731e3feea2dc9e hash_me(dest)
► 0x5af412b8763e <libafl_qemu::modules::edges::gen_hashed_edge_ids+174> xor rax, rcx RAX => 0x4d5543f7dbc53456 (0x4a265dc83567e8c8 ^ 0x7731e3feea2dc9e)
Considering that LIBAFL_QEMU_EDGES_MAP_PTR
is 0x761cc2856000, maybe it causes the SIGSEGV because it exceeds its range after the addition?
715 pub unsafe extern "C" fn trace_edge_hitcount_ptr(_: *const (), id: u64) {
716 unsafe {
717 let ptr = LIBAFL_QEMU_EDGES_MAP_PTR.add(id as usize);
► 718 *ptr = (*ptr).wrapping_add(1);
719 }
720 }
$25 = 0x4d5543f7dbc53456
pwndbg> p/x LIBAFL_QEMU_EDGES_MAP_PTR
$26 = 0x761cc2856000
I tried to change id as usize
to (id as u16).try_into().unwrap(). This part is fine for now. (This is just a temporary solution.) But when I continued, the error Unknown error: Unix error: ECHILD
still occurred. This seems to be because in the run_target
method of GenericInProcessForkExecutorInner
, the parent process does not correctly capture the exit of the child process. I will debug further.
I found that in parent
method of GenericInProcessForkExecutorInner
, waitpid
returned this error. This error, Unknown error: Unix error: ECHILD
, means there is no child process.
pub(super) fn parent(&mut self, child: Pid) -> Result<ExitKind, Error> {
let res = waitpid(child, None)?;
//...
}
The waitpid
in nix calls libc::waitpid
. I searched on search engines and only this question seems to be somewhat related.
The first fork happens in SimpleRestartingEventManager::launch
. The second fork happens in the run_target
function of the Executor called inside load_initial_inputs.
pub fn launch(mut monitor: MT, shmem_provider: &mut SP) -> Result<(Option<S>, Self), Error>
where
S: DeserializeOwned + Serialize + HasCorpus + HasSolutions,
MT: Debug,
{
// Client->parent loop
loop {
log::info!("Spawning next client (id {ctr})");
// On Unix, we fork
#[cfg(all(unix, feature = "fork"))]
let child_status = {
shmem_provider.pre_fork()?;
match unsafe { fork() }? {
ForkResult::Parent(handle) => {
unsafe {
libc::signal(libc::SIGINT, libc::SIG_IGN);
}
shmem_provider.post_fork(false)?;
handle.status()
}
ForkResult::Child => {
shmem_provider.post_fork(true)?;
break staterestorer;
}
}
};
}
fn run_target() -> Result<ExitKind, Error> {
*state.executions_mut() += 1;
unsafe {
self.inner.shmem_provider.pre_fork()?;
match fork() {
Ok(ForkResult::Child) => {
// Child
self.inner.pre_run_target_child(fuzzer, state, mgr, input)?;
(self.harness_fn)(input, &mut self.exposed_executor_state);
self.inner.post_run_target_child(fuzzer, state, mgr, input);
Ok(ExitKind::Ok)
}
Ok(ForkResult::Parent { child }) => {
// Parent
self.inner.parent(child)
}
Err(e) => Err(Error::from(e)),
}
}
}
I am still somewhat confused as to why this happened...
thank you for the detailed report.
I found the process of calculating id and the intermediate value. The calculated id is indeed 0x4d5543f7dbc53456. Do you think there is a problem?
// src is 0x40a23030, dest is 0x40a23058 *RAX 0x4a265dc83567e8c8 hash_me(src) *RAX 0x7731e3feea2dc9e hash_me(dest) ► 0x5af412b8763e <libafl_qemu::modules::edges::gen_hashed_edge_ids+174> xor rax, rcx RAX => 0x4d5543f7dbc53456 (0x4a265dc83567e8c8 ^ 0x7731e3feea2dc9e)
Considering that
LIBAFL_QEMU_EDGES_MAP_PTR
is 0x761cc2856000, maybe it causes the SIGSEGV because it exceeds its range after the addition?715 pub unsafe extern "C" fn trace_edge_hitcount_ptr(_: *const (), id: u64) { 716 unsafe { 717 let ptr = LIBAFL_QEMU_EDGES_MAP_PTR.add(id as usize); ► 718 *ptr = (*ptr).wrapping_add(1); 719 } 720 } $25 = 0x4d5543f7dbc53456 pwndbg> p/x LIBAFL_QEMU_EDGES_MAP_PTR $26 = 0x761cc2856000
about the first bug, could you print the value of
LIBAFL_QEMU_EDGES_MAP_MASK_MAX
once in thegen_hashed_edge_ids
function?I found that in
parent
method ofGenericInProcessForkExecutorInner
,waitpid
returned this error. This error,Unknown error: Unix error: ECHILD
, means there is no child process.pub(super) fn parent(&mut self, child: Pid) -> Result<ExitKind, Error> { let res = waitpid(child, None)?; //... }
The
waitpid
in nix callslibc::waitpid
. I searched on search engines and only this question seems to be somewhat related. The first fork happens inSimpleRestartingEventManager::launch
. The second fork happens in therun_target
function of the Executor called inside load_initial_inputs.pub fn launch(mut monitor: MT, shmem_provider: &mut SP) -> Result<(Option<S>, Self), Error> where S: DeserializeOwned + Serialize + HasCorpus + HasSolutions, MT: Debug, { // Client->parent loop loop { log::info!("Spawning next client (id {ctr})"); // On Unix, we fork #[cfg(all(unix, feature = "fork"))] let child_status = { shmem_provider.pre_fork()?; match unsafe { fork() }? { ForkResult::Parent(handle) => { unsafe { libc::signal(libc::SIGINT, libc::SIG_IGN); } shmem_provider.post_fork(false)?; handle.status() } ForkResult::Child => { shmem_provider.post_fork(true)?; break staterestorer; } } }; } fn run_target() -> Result<ExitKind, Error> { *state.executions_mut() += 1; unsafe { self.inner.shmem_provider.pre_fork()?; match fork() { Ok(ForkResult::Child) => { // Child self.inner.pre_run_target_child(fuzzer, state, mgr, input)?; (self.harness_fn)(input, &mut self.exposed_executor_state); self.inner.post_run_target_child(fuzzer, state, mgr, input); Ok(ExitKind::Ok) } Ok(ForkResult::Parent { child }) => { // Parent self.inner.parent(child) } Err(e) => Err(Error::from(e)), } } }
I am still somewhat confused as to why this happened...
maybe it's about the order in which processes die?
@rmalmain Thank you for your reply.
For the first question, my LIBAFL_QEMU_EDGES_MAP_MASK_MAX
is 0xffff.
About the second one, I write this fuzzer on an httpd program. There are many places in the program that use fork
to process commands. Maybe I should find a more appropriate way to write the fuzzer.
The issue to be present in the current main branch
Describe the issue I am doing some fuzzing practice using an tenda VC 15 router httpd, which is 32-bit arm architecture. I use a QemuForkExecutor, but got an error when load the initial inputs:
I print the error,
and it says:
I debug the fuzzer, and find out that the fuzzer receives a SIGSEGV in trace_edge_hitcount_ptr:
It seems that the value of ptr cannot be dereferenced. I know that this function is used to record the coverage, but I don't know what "id" or "ptr" mean. So I read the related instrumentation code in qemu-libafl-bridge.
My understanding is: if a new translation block is generated by
libafl_gen_edge
, it is executed first, and then it is recorded on the coverage graph by jumping totrace_edge_hitcount_ptr
through the hook. (I use StdEdgeCoverageChildModule, and I remember it used the edge type hook.) Also, I debugged this part of codes. Considering the contents of theTranslationBlock
structure, I found the specific contents of theedge
variable:Note the value of
tc.ptr
here. It is <code_gen_buffer+1811>. The machine code it points to is0x43f7dbc53456be48
, and gdb told me it meansmovabs rsi, 0x4d5543f7dbc53456
. While tracing the code flow later, I found that the fuzzer jumped to a small section of code hook to prepare parameters(moving to rdi and rsi), and then jumped totrace_edge_hitcount_ptr
.This seems to indicate that the number following
movabs rsi,
will become theid
. But the values I have here don't look right.My issues now are as follows:
Thank you very much!