Skip to content

Commit 7680ea1

Browse files
authored
Safe qemu cpu from index (#2941)
* safe cpu from index * add comment
1 parent d1f566c commit 7680ea1

File tree

5 files changed

+74
-43
lines changed

5 files changed

+74
-43
lines changed

fuzzers/full_system/qemu_baremetal/src/fuzzer_low_level.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ pub fn fuzz() {
171171

172172
// If the execution stops at any point other than the designated breakpoint (e.g. a breakpoint on a panic method) we consider it a crash
173173
let mut pcs = (0..qemu.num_cpus())
174-
.map(|i| qemu.cpu_from_index(i))
174+
.map(|i| qemu.cpu_from_index(i).unwrap())
175175
.map(|cpu| -> Result<u32, QemuRWError> { cpu.read_reg(Regs::Pc) });
176176
let ret = match pcs
177177
.find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0)))

libafl_qemu/src/emu/usermode.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ impl<C, CM, ED, ET, I, S, SM> Emulator<C, CM, ED, ET, I, S, SM> {
2222
self.qemu.h2g(addr)
2323
}
2424

25+
/// Checks whether the access at address @addr of size @size is valid.
26+
/// The acess is done relatively to the CPU as described by [`Qemu::access_ok`].
27+
/// If no CPU is found, returns None.
2528
#[must_use]
26-
pub fn access_ok(&self, kind: VerifyAccess, addr: GuestAddr, size: usize) -> bool {
29+
pub fn access_ok(&self, kind: VerifyAccess, addr: GuestAddr, size: usize) -> Option<bool> {
2730
self.qemu.access_ok(kind, addr, size)
2831
}
2932

libafl_qemu/src/qemu/mod.rs

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ pub struct MemAccessInfo {
154154
#[derive(Debug, Clone, Copy)]
155155
#[repr(transparent)]
156156
pub struct CPU {
157-
ptr: CPUStatePtr,
157+
cpu_ptr: CPUStatePtr,
158158
}
159159

160160
#[derive(Debug, Clone, PartialEq)]
@@ -305,20 +305,20 @@ impl CPU {
305305
#[must_use]
306306
#[expect(clippy::cast_sign_loss)]
307307
pub fn index(&self) -> usize {
308-
unsafe { libafl_qemu_cpu_index(self.ptr) as usize }
308+
unsafe { libafl_qemu_cpu_index(self.cpu_ptr) as usize }
309309
}
310310

311311
pub fn trigger_breakpoint(&self) {
312312
unsafe {
313-
libafl_qemu_trigger_breakpoint(self.ptr);
313+
libafl_qemu_trigger_breakpoint(self.cpu_ptr);
314314
}
315315
}
316316

317317
// TODO expose tlb_set_dirty and tlb_reset_dirty
318318

319319
#[must_use]
320320
pub fn num_regs(&self) -> i32 {
321-
unsafe { libafl_qemu_num_regs(self.ptr) }
321+
unsafe { libafl_qemu_num_regs(self.cpu_ptr) }
322322
}
323323

324324
pub fn read_reg<R>(&self, reg: R) -> Result<GuestReg, QemuRWError>
@@ -328,12 +328,12 @@ impl CPU {
328328
unsafe {
329329
let reg_id = reg.clone().into();
330330
let mut val = MaybeUninit::uninit();
331-
let success = libafl_qemu_read_reg(self.ptr, reg_id, val.as_mut_ptr() as *mut u8);
331+
let success = libafl_qemu_read_reg(self.cpu_ptr, reg_id, val.as_mut_ptr() as *mut u8);
332332
if success == 0 {
333333
Err(QemuRWError::wrong_reg(
334334
QemuRWErrorKind::Write,
335335
reg,
336-
Some(self.ptr),
336+
Some(self.cpu_ptr),
337337
))
338338
} else {
339339
#[cfg(feature = "be")]
@@ -357,12 +357,13 @@ impl CPU {
357357
#[cfg(not(feature = "be"))]
358358
let val = GuestReg::to_le(val.into());
359359

360-
let success = unsafe { libafl_qemu_write_reg(self.ptr, reg_id, &raw const val as *mut u8) };
360+
let success =
361+
unsafe { libafl_qemu_write_reg(self.cpu_ptr, reg_id, &raw const val as *mut u8) };
361362
if success == 0 {
362363
Err(QemuRWError::wrong_reg(
363364
QemuRWErrorKind::Write,
364365
reg,
365-
Some(self.ptr),
366+
Some(self.cpu_ptr),
366367
))
367368
} else {
368369
Ok(())
@@ -374,7 +375,7 @@ impl CPU {
374375
// TODO use gdbstub's target_cpu_memory_rw_debug
375376
let ret = unsafe {
376377
libafl_qemu_sys::cpu_memory_rw_debug(
377-
self.ptr,
378+
self.cpu_ptr,
378379
addr as GuestVirtAddr,
379380
buf.as_mut_ptr() as *mut _,
380381
buf.len(),
@@ -385,7 +386,7 @@ impl CPU {
385386
if ret != 0 {
386387
Err(QemuRWError::wrong_mem_location(
387388
QemuRWErrorKind::Read,
388-
self.ptr,
389+
self.cpu_ptr,
389390
addr,
390391
buf.len(),
391392
))
@@ -411,7 +412,7 @@ impl CPU {
411412
// TODO use gdbstub's target_cpu_memory_rw_debug
412413
let ret = unsafe {
413414
libafl_qemu_sys::cpu_memory_rw_debug(
414-
self.ptr,
415+
self.cpu_ptr,
415416
addr as GuestVirtAddr,
416417
buf.as_ptr() as *mut _,
417418
buf.len(),
@@ -422,7 +423,7 @@ impl CPU {
422423
if ret != 0 {
423424
Err(QemuRWError::wrong_mem_location(
424425
QemuRWErrorKind::Write,
425-
self.ptr,
426+
self.cpu_ptr,
426427
addr,
427428
buf.len(),
428429
))
@@ -432,15 +433,15 @@ impl CPU {
432433
}
433434

434435
pub fn reset(&self) {
435-
unsafe { libafl_qemu_sys::cpu_reset(self.ptr) };
436+
unsafe { libafl_qemu_sys::cpu_reset(self.cpu_ptr) };
436437
}
437438

438439
#[must_use]
439440
pub fn save_state(&self) -> CPUArchState {
440441
unsafe {
441442
let mut saved = MaybeUninit::<CPUArchState>::uninit();
442443
copy_nonoverlapping(
443-
libafl_qemu_sys::cpu_env(self.ptr.as_mut().unwrap()),
444+
libafl_qemu_sys::cpu_env(self.cpu_ptr.as_mut().unwrap()),
444445
saved.as_mut_ptr(),
445446
1,
446447
);
@@ -452,15 +453,15 @@ impl CPU {
452453
unsafe {
453454
copy_nonoverlapping(
454455
saved,
455-
libafl_qemu_sys::cpu_env(self.ptr.as_mut().unwrap()),
456+
libafl_qemu_sys::cpu_env(self.cpu_ptr.as_mut().unwrap()),
456457
1,
457458
);
458459
}
459460
}
460461

461462
#[must_use]
462463
pub fn raw_ptr(&self) -> CPUStatePtr {
463-
self.ptr
464+
self.cpu_ptr
464465
}
465466

466467
#[must_use]
@@ -737,43 +738,62 @@ impl Qemu {
737738
if ptr.is_null() {
738739
None
739740
} else {
740-
Some(CPU { ptr })
741+
Some(CPU { cpu_ptr: ptr })
741742
}
742743
}
743744

744745
#[must_use]
745746
#[expect(clippy::cast_possible_wrap)]
746-
pub fn cpu_from_index(&self, index: usize) -> CPU {
747-
unsafe {
748-
CPU {
749-
ptr: libafl_qemu_get_cpu(index as i32),
750-
}
747+
pub fn cpu_from_index(&self, index: usize) -> Option<CPU> {
748+
let cpu_ptr = unsafe { libafl_qemu_get_cpu(index as i32) };
749+
750+
if cpu_ptr.is_null() {
751+
None
752+
} else {
753+
Some(CPU { cpu_ptr })
751754
}
752755
}
753756

757+
/// # Safety
758+
///
759+
/// CPU will be incorrect if CPU index does not exist
760+
#[must_use]
761+
#[expect(clippy::cast_possible_wrap)]
762+
pub unsafe fn cpu_from_index_unchecked(&self, index: usize) -> CPU {
763+
let cpu_ptr = unsafe { libafl_qemu_get_cpu(index as i32) };
764+
765+
CPU { cpu_ptr }
766+
}
767+
754768
#[must_use]
755769
pub fn page_from_addr(&self, addr: GuestAddr) -> GuestAddr {
756770
unsafe { libafl_page_from_addr(addr) }
757771
}
758772

759773
/// Read a value from a guest address, taking into account the potential indirections with the current CPU.
774+
/// Uses the 0th CPU if no CPU is currently running.
760775
pub fn read_mem(&self, addr: GuestAddr, buf: &mut [u8]) -> Result<(), QemuRWError> {
761776
self.current_cpu()
762-
.unwrap_or_else(|| self.cpu_from_index(0))
777+
.or_else(|| self.cpu_from_index(0))
778+
.ok_or(QemuRWError::current_cpu_not_found(QemuRWErrorKind::Read))?
763779
.read_mem(addr, buf)
764780
}
765781

766782
/// Read a value from a guest address, taking into account the potential indirections with the current CPU.
783+
/// Uses the 0th CPU if no CPU is currently running.
767784
pub fn read_mem_vec(&self, addr: GuestAddr, len: usize) -> Result<Vec<u8>, QemuRWError> {
768785
self.current_cpu()
769-
.unwrap_or_else(|| self.cpu_from_index(0))
786+
.or_else(|| self.cpu_from_index(0))
787+
.ok_or(QemuRWError::current_cpu_not_found(QemuRWErrorKind::Read))?
770788
.read_mem_vec(addr, len)
771789
}
772790

773791
/// Write a value to a guest address, taking into account the potential indirections with the current CPU.
792+
/// Uses the 0th CPU if no CPU is currently running.
774793
pub fn write_mem(&self, addr: GuestAddr, buf: &[u8]) -> Result<(), QemuRWError> {
775794
self.current_cpu()
776-
.unwrap_or_else(|| self.cpu_from_index(0))
795+
.or_else(|| self.cpu_from_index(0))
796+
.ok_or(QemuRWError::current_cpu_not_found(QemuRWErrorKind::Write))?
777797
.write_mem(addr, buf)
778798
}
779799

@@ -816,12 +836,12 @@ impl Qemu {
816836
/// This may only be safely used for valid guest addresses.
817837
///
818838
/// In any case, no check will be performed on the correctness of the operation.
819-
///
839+
/// Also, the there must be a current CPU (or a CPU at index 0).
820840
/// Please refer to [`CPU::read_mem`] for more details.
821841
pub unsafe fn read_mem_unchecked(&self, addr: GuestAddr, buf: &mut [u8]) {
822842
unsafe {
823843
self.current_cpu()
824-
.unwrap_or_else(|| self.cpu_from_index(0))
844+
.unwrap_or_else(|| self.cpu_from_index_unchecked(0))
825845
.read_mem_unchecked(addr, buf);
826846
}
827847
}
@@ -834,11 +854,12 @@ impl Qemu {
834854
/// In any case, no check will be performed on the correctness of the operation.
835855
///
836856
/// This may only be safely used for valid guest addresses.
857+
/// Also, the there must be a current CPU (or a CPU at index 0).
837858
/// Please refer to [`CPU::write_mem`] for more details.
838859
pub unsafe fn write_mem_unchecked(&self, addr: GuestAddr, buf: &[u8]) {
839860
unsafe {
840861
self.current_cpu()
841-
.unwrap_or_else(|| self.cpu_from_index(0))
862+
.unwrap_or_else(|| self.cpu_from_index_unchecked(0))
842863
.write_mem_unchecked(addr, buf);
843864
}
844865
}

libafl_qemu/src/qemu/systemmode.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ impl CPU {
9292
let page = libafl_page_from_addr(vaddr as GuestUsize) as GuestVirtAddr;
9393
let mut attrs = MaybeUninit::<libafl_qemu_sys::MemTxAttrs>::uninit();
9494
let paddr = libafl_qemu_sys::cpu_get_phys_page_attrs_debug(
95-
self.ptr,
95+
self.cpu_ptr,
9696
page as GuestVirtAddr,
9797
attrs.as_mut_ptr(),
9898
);
@@ -134,7 +134,7 @@ impl CPU {
134134

135135
#[must_use]
136136
pub fn current_paging_id(&self) -> Option<GuestPhysAddr> {
137-
let paging_id = unsafe { libafl_qemu_current_paging_id(self.ptr) };
137+
let paging_id = unsafe { libafl_qemu_current_paging_id(self.cpu_ptr) };
138138

139139
if paging_id == 0 {
140140
None
@@ -149,10 +149,10 @@ impl CPU {
149149
/// no check is done on the correctness of the operation.
150150
/// if a problem occurred during the operation, there will be no feedback
151151
pub unsafe fn read_mem_unchecked(&self, addr: GuestAddr, buf: &mut [u8]) {
152-
// TODO use gdbstub's target_cpu_memory_rw_debug
153152
unsafe {
153+
// TODO use gdbstub's target_cpu_memory_rw_debug
154154
libafl_qemu_sys::cpu_memory_rw_debug(
155-
self.ptr,
155+
self.cpu_ptr,
156156
addr as GuestVirtAddr,
157157
buf.as_mut_ptr() as *mut _,
158158
buf.len(),
@@ -167,10 +167,10 @@ impl CPU {
167167
/// no check is done on the correctness of the operation.
168168
/// if a problem occurred during the operation, there will be no feedback
169169
pub unsafe fn write_mem_unchecked(&self, addr: GuestAddr, buf: &[u8]) {
170-
// TODO use gdbstub's target_cpu_memory_rw_debug
171170
unsafe {
171+
// TODO use gdbstub's target_cpu_memory_rw_debug
172172
libafl_qemu_sys::cpu_memory_rw_debug(
173-
self.ptr,
173+
self.cpu_ptr,
174174
addr as GuestVirtAddr,
175175
buf.as_ptr() as *mut _,
176176
buf.len(),
@@ -376,7 +376,7 @@ impl<'a> Iterator for HostMemoryIter<'a> {
376376
} else {
377377
// Host memory allocation is always host-page aligned, so we can freely go from host page to host page.
378378
let start_host_addr: *const u8 =
379-
unsafe { libafl_qemu_sys::libafl_paddr2host(self.cpu.ptr, self.addr, false) };
379+
unsafe { libafl_qemu_sys::libafl_paddr2host(self.cpu.cpu_ptr, self.addr, false) };
380380
let host_page_size = Qemu::get().unwrap().host_page_size();
381381
let mut size_taken: usize = std::cmp::min(
382382
(start_host_addr as usize).next_multiple_of(host_page_size),
@@ -388,8 +388,9 @@ impl<'a> Iterator for HostMemoryIter<'a> {
388388

389389
// Now self.addr is host-page aligned
390390
while self.remaining_len > 0 {
391-
let next_page_host_addr: *const u8 =
392-
unsafe { libafl_qemu_sys::libafl_paddr2host(self.cpu.ptr, self.addr, false) };
391+
let next_page_host_addr: *const u8 = unsafe {
392+
libafl_qemu_sys::libafl_paddr2host(self.cpu.cpu_ptr, self.addr, false)
393+
};
393394

394395
// Non-contiguous, we stop here for the slice
395396
if next_page_host_addr != start_host_addr {

libafl_qemu/src/qemu/usermode.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,17 @@ impl Qemu {
264264
unsafe { (addr as usize - guest_base) as GuestAddr }
265265
}
266266

267+
/// Tells whether access to target address @addr of size @size is valid or not.
268+
/// The access is checked relatively to `current_cpu` if available, or the CPU at index 0
269+
/// otherwise.
270+
/// The function returns None if no CPU could be found.
267271
#[must_use]
268-
pub fn access_ok(&self, kind: VerifyAccess, addr: GuestAddr, size: usize) -> bool {
269-
self.current_cpu()
270-
.unwrap_or_else(|| self.cpu_from_index(0))
271-
.access_ok(kind, addr, size)
272+
pub fn access_ok(&self, kind: VerifyAccess, addr: GuestAddr, size: usize) -> Option<bool> {
273+
Some(
274+
self.current_cpu()
275+
.or_else(|| self.cpu_from_index(0))?
276+
.access_ok(kind, addr, size),
277+
)
272278
}
273279

274280
pub fn force_dfl(&self) {

0 commit comments

Comments
 (0)