Skip to content

Commit 78fd4e0

Browse files
authored
frida-asan: move to mmap-rs (#1570)
1 parent 9c3f8f4 commit 78fd4e0

File tree

2 files changed

+98
-112
lines changed

2 files changed

+98
-112
lines changed

libafl_frida/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ num-traits = "0.2"
7272
ahash = "0.8"
7373
paste = "1.0"
7474
log = "0.4.20"
75+
mmap-rs = "0.6.0"
7576

7677
[dev-dependencies]
7778
serial_test = { version = "2", default-features = false, features = ["logging"] }

libafl_frida/src/alloc.rs

Lines changed: 97 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
target_vendor = "apple",
44
all(target_arch = "aarch64", target_os = "android")
55
))]
6-
use std::io;
7-
use std::{collections::BTreeMap, ffi::c_void, num::NonZeroUsize};
6+
use std::{collections::BTreeMap, ffi::c_void};
87

98
use backtrace::Backtrace;
109
use frida_gum::{PageProtection, RangeDetails};
@@ -15,11 +14,8 @@ use libafl_bolts::cli::FuzzerOptions;
1514
target_vendor = "apple",
1615
all(target_arch = "aarch64", target_os = "android")
1716
))]
18-
use libc::{sysconf, _SC_PAGESIZE};
19-
use nix::{
20-
libc::memset,
21-
sys::mman::{mmap, MapFlags, ProtFlags},
22-
};
17+
use mmap_rs::{MemoryAreas, MmapFlags, MmapMut, MmapOptions, ReservedMut};
18+
use nix::libc::memset;
2319
use rangemap::RangeSet;
2420
use serde::{Deserialize, Serialize};
2521

@@ -38,10 +34,12 @@ pub struct Allocator {
3834
shadow_offset: usize,
3935
/// The shadow bit
4036
shadow_bit: usize,
41-
/// If the shadow is pre-allocated
42-
pre_allocated_shadow: bool,
37+
/// The reserved (pre-allocated) shadow mapping
38+
pre_allocated_shadow_mappings: HashMap<(usize, usize), ReservedMut>,
4339
/// All tracked allocations
4440
allocations: HashMap<usize, AllocationMetadata>,
41+
/// All mappings
42+
mappings: HashMap<usize, MmapMut>,
4543
/// The shadow memory pages
4644
shadow_pages: RangeSet<usize>,
4745
/// A list of allocations
@@ -56,11 +54,6 @@ pub struct Allocator {
5654
current_mapping_addr: usize,
5755
}
5856

59-
#[cfg(target_vendor = "apple")]
60-
const ANONYMOUS_FLAG: MapFlags = MapFlags::MAP_ANON;
61-
#[cfg(not(target_vendor = "apple"))]
62-
const ANONYMOUS_FLAG: MapFlags = MapFlags::MAP_ANONYMOUS;
63-
6457
macro_rules! map_to_shadow {
6558
($self:expr, $address:expr) => {
6659
$self.shadow_offset + (($address >> 3) & ((1 << ($self.shadow_bit + 1)) - 1))
@@ -89,6 +82,7 @@ pub struct AllocationMetadata {
8982
impl Allocator {
9083
/// Creates a new [`Allocator`] (not supported on this platform!)
9184
#[cfg(not(any(
85+
windows,
9286
target_os = "linux",
9387
target_vendor = "apple",
9488
all(target_arch = "aarch64", target_os = "android")
@@ -100,6 +94,7 @@ impl Allocator {
10094

10195
/// Creates a new [`Allocator`]
10296
#[cfg(any(
97+
windows,
10398
target_os = "linux",
10499
target_vendor = "apple",
105100
all(target_arch = "aarch64", target_os = "android")
@@ -181,29 +176,32 @@ impl Allocator {
181176
metadata
182177
} else {
183178
// log::trace!("{:x}, {:x}", self.current_mapping_addr, rounded_up_size);
184-
let mapping = match mmap(
185-
NonZeroUsize::new(self.current_mapping_addr),
186-
NonZeroUsize::new_unchecked(rounded_up_size),
187-
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
188-
ANONYMOUS_FLAG
189-
| MapFlags::MAP_PRIVATE
190-
| MapFlags::MAP_FIXED
191-
| MapFlags::MAP_NORESERVE,
192-
-1,
193-
0,
194-
) {
195-
Ok(mapping) => mapping as usize,
179+
let mapping = match MmapOptions::new(rounded_up_size)
180+
.unwrap()
181+
.with_address(self.current_mapping_addr)
182+
.map_mut()
183+
{
184+
Ok(mapping) => mapping,
196185
Err(err) => {
197186
log::error!("An error occurred while mapping memory: {err:?}");
198187
return std::ptr::null_mut();
199188
}
200189
};
201-
self.current_mapping_addr += rounded_up_size;
202-
203-
self.map_shadow_for_region(mapping, mapping + rounded_up_size, false);
190+
self.current_mapping_addr += ((rounded_up_size
191+
+ MmapOptions::allocation_granularity())
192+
/ MmapOptions::allocation_granularity())
193+
* MmapOptions::allocation_granularity();
194+
195+
self.map_shadow_for_region(
196+
mapping.as_ptr() as usize,
197+
mapping.as_ptr().add(rounded_up_size) as usize,
198+
false,
199+
);
200+
let address = mapping.as_ptr() as usize;
201+
self.mappings.insert(address, mapping);
204202

205203
let mut metadata = AllocationMetadata {
206-
address: mapping,
204+
address,
207205
size,
208206
actual_size: rounded_up_size,
209207
..AllocationMetadata::default()
@@ -223,8 +221,7 @@ impl Allocator {
223221
);
224222
let address = (metadata.address + self.page_size) as *mut c_void;
225223

226-
self.allocations
227-
.insert(metadata.address + self.page_size, metadata);
224+
self.allocations.insert(address as usize, metadata);
228225
// log::trace!("serving address: {:?}, size: {:x}", address, size);
229226
address
230227
}
@@ -373,31 +370,50 @@ impl Allocator {
373370

374371
let shadow_mapping_start = map_to_shadow!(self, start);
375372

376-
if !self.pre_allocated_shadow {
377-
let shadow_start = self.round_down_to_page(shadow_mapping_start);
378-
let shadow_end =
379-
self.round_up_to_page((end - start) / 8) + self.page_size + shadow_start;
373+
let shadow_start = self.round_down_to_page(shadow_mapping_start);
374+
let shadow_end = self.round_up_to_page((end - start) / 8) + self.page_size + shadow_start;
375+
if self.pre_allocated_shadow_mappings.is_empty() {
380376
for range in self.shadow_pages.gaps(&(shadow_start..shadow_end)) {
381377
/*
382378
log::trace!(
383379
"range: {:x}-{:x}, pagesize: {}",
384380
range.start, range.end, self.page_size
385381
);
386382
*/
387-
unsafe {
388-
mmap(
389-
NonZeroUsize::new(range.start),
390-
NonZeroUsize::new(range.end - range.start).unwrap(),
391-
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
392-
ANONYMOUS_FLAG | MapFlags::MAP_FIXED | MapFlags::MAP_PRIVATE,
393-
-1,
394-
0,
395-
)
383+
let mapping = MmapOptions::new(range.end - range.start - 1)
384+
.unwrap()
385+
.with_address(range.start)
386+
.map_mut()
396387
.expect("An error occurred while mapping shadow memory");
397-
}
388+
389+
self.mappings.insert(range.start, mapping);
398390
}
399391

400392
self.shadow_pages.insert(shadow_start..shadow_end);
393+
} else {
394+
let mut new_shadow_mappings = Vec::new();
395+
for range in self.shadow_pages.gaps(&(shadow_start..shadow_end)) {
396+
for ((start, end), shadow_mapping) in &mut self.pre_allocated_shadow_mappings {
397+
if *start <= range.start && range.start < *start + shadow_mapping.len() {
398+
let mut start_mapping =
399+
shadow_mapping.split_off(range.start - *start).unwrap();
400+
let end_mapping = start_mapping
401+
.split_off(range.end - (range.start - *start))
402+
.unwrap();
403+
new_shadow_mappings.push(((range.end, *end), end_mapping));
404+
self.mappings
405+
.insert(range.start, start_mapping.try_into().unwrap());
406+
407+
break;
408+
}
409+
}
410+
}
411+
for new_shadow_mapping in new_shadow_mappings {
412+
self.pre_allocated_shadow_mappings
413+
.insert(new_shadow_mapping.0, new_shadow_mapping.1);
414+
self.shadow_pages
415+
.insert(new_shadow_mapping.0 .0..new_shadow_mapping.0 .1);
416+
}
401417
}
402418

403419
// log::trace!("shadow_mapping_start: {:x}, shadow_size: {:x}", shadow_mapping_start, (end - start) / 8);
@@ -438,7 +454,7 @@ impl Allocator {
438454
if range.protection() as u32 & PageProtection::ReadWrite as u32 != 0 {
439455
let start = range.memory_range().base_address().0 as usize;
440456
let end = start + range.memory_range().size();
441-
if self.pre_allocated_shadow && start == 1 << self.shadow_bit {
457+
if !self.pre_allocated_shadow_mappings.is_empty() && start == 1 << self.shadow_bit {
442458
return true;
443459
}
444460
self.map_shadow_for_region(start, end, true);
@@ -461,31 +477,28 @@ impl Allocator {
461477
let mut userspace_max: usize = 0;
462478

463479
// Enumerate memory ranges that are already occupied.
464-
for prot in [
465-
PageProtection::Read,
466-
PageProtection::Write,
467-
PageProtection::Execute,
468-
] {
469-
RangeDetails::enumerate_with_prot(prot, &mut |details| {
470-
let start = details.memory_range().base_address().0 as usize;
471-
let end = start + details.memory_range().size();
472-
occupied_ranges.push((start, end));
473-
log::trace!("{:x} {:x}", start, end);
474-
let base: usize = 2;
475-
// On x64, if end > 2**48, then that's in vsyscall or something.
476-
#[cfg(target_arch = "x86_64")]
477-
if end <= base.pow(48) && end > userspace_max {
478-
userspace_max = end;
479-
}
480+
for area in MemoryAreas::open(None).unwrap() {
481+
let start = area.as_ref().unwrap().start();
482+
let end = area.unwrap().end();
483+
occupied_ranges.push((start, end));
484+
log::trace!("{:x} {:x}", start, end);
485+
let base: usize = 2;
486+
// On x64, if end > 2**48, then that's in vsyscall or something.
487+
#[cfg(all(unix, target_arch = "x86_64"))]
488+
if end <= base.pow(48) && end > userspace_max {
489+
userspace_max = end;
490+
}
480491

481-
// On x64, if end > 2**52, then range is not in userspace
482-
#[cfg(target_arch = "aarch64")]
483-
if end <= base.pow(52) && end > userspace_max {
484-
userspace_max = end;
485-
}
492+
#[cfg(all(not(unix), target_arch = "x86_64"))]
493+
if (end >> 3) <= base.pow(44) && (end >> 3) > userspace_max {
494+
userspace_max = end >> 3;
495+
}
486496

487-
true
488-
});
497+
// On aarch64, if end > 2**52, then range is not in userspace
498+
#[cfg(target_arch = "aarch64")]
499+
if end <= base.pow(52) && end > userspace_max {
500+
userspace_max = end;
501+
}
489502
}
490503

491504
let mut maxbit = 0;
@@ -498,7 +511,7 @@ impl Allocator {
498511
}
499512

500513
{
501-
for try_shadow_bit in &[maxbit - 4, maxbit - 3, maxbit - 2] {
514+
for try_shadow_bit in &[maxbit, maxbit - 4, maxbit - 3, maxbit - 2] {
502515
let addr: usize = 1 << try_shadow_bit;
503516
let shadow_start = addr;
504517
let shadow_end = addr + addr + addr;
@@ -512,48 +525,27 @@ impl Allocator {
512525
}
513526
}
514527

515-
if unsafe {
516-
mmap(
517-
NonZeroUsize::new(addr),
518-
NonZeroUsize::new_unchecked(self.page_size),
519-
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
520-
MapFlags::MAP_PRIVATE
521-
| ANONYMOUS_FLAG
522-
| MapFlags::MAP_FIXED
523-
| MapFlags::MAP_NORESERVE,
524-
-1,
525-
0,
526-
)
527-
}
528-
.is_ok()
528+
if let Ok(mapping) = MmapOptions::new(1 << (*try_shadow_bit + 1))
529+
.unwrap()
530+
.with_flags(MmapFlags::NO_RESERVE)
531+
.with_address(addr)
532+
.reserve_mut()
529533
{
530534
shadow_bit = (*try_shadow_bit).try_into().unwrap();
535+
536+
log::warn!("shadow_bit {shadow_bit:x} is suitable");
537+
self.pre_allocated_shadow_mappings
538+
.insert((addr, (addr + (1 << shadow_bit))), mapping);
531539
break;
532540
}
533541
}
534542
}
535543

536-
log::warn!("shadow_bit {shadow_bit:x} is suitable");
537544
// assert!(shadow_bit != 0);
538545
// attempt to pre-map the entire shadow-memory space
539546

540547
let addr: usize = 1 << shadow_bit;
541-
let pre_allocated_shadow = unsafe {
542-
mmap(
543-
NonZeroUsize::new(addr),
544-
NonZeroUsize::new_unchecked(addr + addr),
545-
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
546-
ANONYMOUS_FLAG
547-
| MapFlags::MAP_FIXED
548-
| MapFlags::MAP_PRIVATE
549-
| MapFlags::MAP_NORESERVE,
550-
-1,
551-
0,
552-
)
553-
}
554-
.is_ok();
555548

556-
self.pre_allocated_shadow = pre_allocated_shadow;
557549
self.shadow_offset = 1 << shadow_bit;
558550
self.shadow_bit = shadow_bit;
559551
self.base_mapping_addr = addr + addr + addr;
@@ -564,6 +556,7 @@ impl Allocator {
564556
impl Default for Allocator {
565557
/// Creates a new [`Allocator`] (not supported on this platform!)
566558
#[cfg(not(any(
559+
windows,
567560
target_os = "linux",
568561
target_vendor = "apple",
569562
all(target_arch = "aarch64", target_os = "android")
@@ -572,25 +565,17 @@ impl Default for Allocator {
572565
todo!("Shadow region not yet supported for this platform!");
573566
}
574567

575-
#[allow(clippy::too_many_lines)]
576568
fn default() -> Self {
577-
let ret = unsafe { sysconf(_SC_PAGESIZE) };
578-
assert!(
579-
ret >= 0,
580-
"Failed to read pagesize {:?}",
581-
io::Error::last_os_error()
582-
);
583-
584-
#[allow(clippy::cast_sign_loss)]
585-
let page_size = ret as usize;
569+
let page_size = MmapOptions::page_size();
586570

587571
Self {
588572
max_allocation: 1 << 30,
589573
max_allocation_panics: false,
590574
max_total_allocation: 1 << 32,
591575
allocation_backtraces: false,
592576
page_size,
593-
pre_allocated_shadow: false,
577+
pre_allocated_shadow_mappings: HashMap::new(),
578+
mappings: HashMap::new(),
594579
shadow_offset: 0,
595580
shadow_bit: 0,
596581
allocations: HashMap::new(),

0 commit comments

Comments
 (0)