Skip to content
This repository was archived by the owner on Mar 24, 2022. It is now read-only.

Commit 2ec5184

Browse files
committed
[lucet-runtime] make init/reset faster for large initial heaps
Rather than explicitly zeroing, use `madvise` and `mprotect` to zero the heap. We also now make sure the heap address space is at least as big as the memory size when validating `Limits`.
1 parent 0ae1d5a commit 2ec5184

File tree

4 files changed

+49
-32
lines changed

4 files changed

+49
-32
lines changed

lucet-runtime/lucet-runtime-internals/src/alloc/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,11 @@ impl Limits {
388388
"address space size must be a multiple of host page size",
389389
));
390390
}
391+
if self.heap_memory_size > self.heap_address_space_size {
392+
return Err(Error::InvalidArgument(
393+
"address space size must be at least as large as memory size",
394+
));
395+
}
391396
if self.stack_size % host_page_size() != 0 {
392397
return Err(Error::InvalidArgument(
393398
"stack size must be a multiple of host page size",

lucet-runtime/lucet-runtime-internals/src/alloc/tests.rs

+13
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,19 @@ macro_rules! alloc_tests {
256256
assert!(res.is_err(), "new_instance fails");
257257
}
258258

259+
/// This test shows that we reject limits with a larger memory size than address space size
260+
#[test]
261+
fn reject_undersized_address_space() {
262+
const LIMITS: Limits = Limits {
263+
heap_memory_size: LIMITS_HEAP_ADDRSPACE_SIZE + 4096,
264+
heap_address_space_size: LIMITS_HEAP_ADDRSPACE_SIZE,
265+
stack_size: LIMITS_STACK_SIZE,
266+
globals_size: LIMITS_GLOBALS_SIZE,
267+
};
268+
let res = TestRegion::create(10, &LIMITS);
269+
assert!(res.is_err(), "region creation fails");
270+
}
271+
259272
const SMALL_GUARD_HEAP: HeapSpec = HeapSpec {
260273
reserved_size: SPEC_HEAP_RESERVED_SIZE,
261274
guard_size: SPEC_HEAP_GUARD_SIZE - 1,

lucet-runtime/lucet-runtime-internals/src/region/mmap.rs

+23-26
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ impl RegionInternal for MmapRegion {
3939
module.validate_runtime_spec(limits)?;
4040

4141
for (ptr, len) in [
42-
// make the heap read/writable and record its initial size
43-
(slot.heap, module.heap_spec().initial_size as usize),
4442
// make the stack read/writable
4543
(slot.stack, limits.stack_size),
4644
// make the globals read/writable
@@ -54,6 +52,8 @@ impl RegionInternal for MmapRegion {
5452
unsafe { mprotect(*ptr, *len, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE)? };
5553
}
5654

55+
// note: the initial heap will be made read/writable when `new_instance_handle` calls `reset`
56+
5757
let inst_ptr = slot.start as *mut Instance;
5858

5959
// upgrade the slot's weak region pointer so the region can't get dropped while the instance
@@ -66,7 +66,7 @@ impl RegionInternal for MmapRegion {
6666
.expect("backing region of slot (`self`) exists");
6767

6868
let alloc = Alloc {
69-
heap_accessible_size: module.heap_spec().initial_size as usize,
69+
heap_accessible_size: 0, // the `reset` call in `new_instance_handle` will set this
7070
heap_inaccessible_size: slot.limits.heap_address_space_size,
7171
slot: Some(slot),
7272
region,
@@ -119,29 +119,32 @@ impl RegionInternal for MmapRegion {
119119
}
120120

121121
fn reset_heap(&self, alloc: &mut Alloc, module: &dyn Module) -> Result<(), Error> {
122+
let heap = alloc.slot().heap;
123+
124+
if alloc.heap_accessible_size > 0 {
125+
// zero the whole heap, if any of it is currently accessible
126+
let heap_size = alloc.slot().limits.heap_address_space_size;
127+
128+
unsafe {
129+
mprotect(heap, heap_size, ProtFlags::PROT_NONE)?;
130+
madvise(heap, heap_size, MmapAdvise::MADV_DONTNEED)?;
131+
}
132+
}
133+
122134
let initial_size = module.heap_spec().initial_size as usize;
123135

124-
// reset the heap to the initial size
136+
// reset the heap to the initial size, and mprotect those pages appropriately
125137
if alloc.heap_accessible_size != initial_size {
138+
unsafe {
139+
mprotect(
140+
heap,
141+
initial_size,
142+
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
143+
)?
144+
};
126145
alloc.heap_accessible_size = initial_size;
127146
alloc.heap_inaccessible_size =
128147
alloc.slot().limits.heap_address_space_size - initial_size;
129-
130-
// turn off any extra pages
131-
let acc_heap_end =
132-
(alloc.slot().heap as usize + alloc.heap_accessible_size) as *mut c_void;
133-
unsafe {
134-
mprotect(
135-
acc_heap_end,
136-
alloc.heap_inaccessible_size,
137-
ProtFlags::PROT_NONE,
138-
)?;
139-
madvise(
140-
acc_heap_end,
141-
alloc.heap_inaccessible_size,
142-
MmapAdvise::MADV_DONTNEED,
143-
)?;
144-
}
145148
}
146149

147150
// Initialize the heap using the module sparse page data. There cannot be more pages in the
@@ -171,12 +174,6 @@ impl RegionInternal for MmapRegion {
171174
if let Some(contents) = module.get_sparse_page_data(page_num) {
172175
// otherwise copy in the page data
173176
heap[page_base..page_base + host_page_size()].copy_from_slice(contents);
174-
} else {
175-
// zero this page
176-
// TODO would using a memset primitive be better here?
177-
for b in heap[page_base..page_base + host_page_size()].iter_mut() {
178-
*b = 0x00;
179-
}
180177
}
181178
}
182179

lucet-wasi/src/main.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,7 @@ fn main() {
6969
.long("heap-address-space")
7070
.takes_value(true)
7171
.default_value("8 GiB")
72-
.help("Maximum heap address space size (must be a multiple of 4 KiB)")
73-
.long_help(
74-
"Maximum heap address space size. Must be a multiple of 4KiB, and \
75-
must be least as large as `max-heap-size`, `stack-size`, and \
76-
`globals-size`, combined.",
77-
),
72+
.help("Maximum heap address space size (must be a multiple of 4 KiB, and >= `max-heap-size`)"),
7873
)
7974
.arg(
8075
Arg::with_name("stack_size")
@@ -129,6 +124,13 @@ fn main() {
129124
.unwrap_or_else(|e| e.exit())
130125
.into::<Byte>()
131126
.value() as usize;
127+
128+
if heap_memory_size > heap_address_space_size {
129+
println!("`heap-address-space` must be at least as large as `max-heap-size`");
130+
println!("{}", matches.usage());
131+
std::process::exit(1);
132+
}
133+
132134
let stack_size = value_t!(matches, "stack_size", Size)
133135
.unwrap_or_else(|e| e.exit())
134136
.into::<Byte>()

0 commit comments

Comments
 (0)