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

Commit 34eca38

Browse files
acfoltzertyler
authored andcommitted
enable alloc tests for UffdRegion, add new test, fix some things
- Instantiates the suite in `lucet_runtime_internals::alloc::tests` for `UffdRegion - Fixes early return issues in `UffdRegion` similar to #455 - Adds a test to show that the per-instance heap limit applies to runtime expansions, not just initial instantiation - Refactors `validate_runtime_spec` to take the per-instance heap limit as an additional argument. This centralizes the logic for rejecting initially-oversized heap limits, and makes it clearer what's happening in each region's instantiation logic. - Removes the `UffdRegion`'s assertion that signal stack size is a multiple of page size. Since the user can now control this as a parameter, we reject it gracefully when validating `Limits` rather than panicking.
1 parent 6bb51bd commit 34eca38

4 files changed

Lines changed: 69 additions & 41 deletions

File tree

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

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,9 @@ macro_rules! alloc_tests {
243243
max_size: Some(EXPANDPASTLIMIT_MAX_SIZE),
244244
};
245245

246-
/// This test shows that a heap refuses to grow past the alloc limits, even if the runtime
247-
/// spec says it can grow bigger. This test uses a region with multiple slots in order to
248-
/// exercise more edge cases with adjacent managed memory.
246+
/// This test shows that a heap refuses to grow past the region's limits, even if the
247+
/// runtime spec says it can grow bigger. This test uses a region with multiple slots in
248+
/// order to exercise more edge cases with adjacent managed memory.
249249
#[test]
250250
fn expand_past_heap_limit() {
251251
let region = TestRegion::create(10, &LIMITS).expect("region created");
@@ -283,6 +283,55 @@ macro_rules! alloc_tests {
283283
assert_eq!(heap[new_heap_len - 1], 0xFF);
284284
}
285285

286+
/// This test shows that a heap refuses to grow past the instance-specific heap limit, even
287+
/// if both the region's limits and the runtime spec says it can grow bigger. This test uses
288+
/// a region with multiple slots in order to exercise more edge cases with adjacent managed
289+
/// memory.
290+
#[test]
291+
fn expand_past_instance_heap_limit() {
292+
const INSTANCE_LIMIT: usize = LIMITS.heap_memory_size / 2;
293+
const SPEC: HeapSpec = HeapSpec {
294+
initial_size: INSTANCE_LIMIT as u64 - 64 * 1024,
295+
..EXPAND_PAST_LIMIT_SPEC
296+
};
297+
assert_eq!(INSTANCE_LIMIT % host_page_size(), 0);
298+
299+
let region = TestRegion::create(10, &LIMITS).expect("region created");
300+
let module = MockModuleBuilder::new().with_heap_spec(SPEC).build();
301+
let mut inst = region
302+
.new_instance_builder(module.clone())
303+
.with_heap_size_limit(INSTANCE_LIMIT)
304+
.build()
305+
.expect("instantiation succeeds");
306+
307+
let heap_len = inst.alloc().heap_len();
308+
assert_eq!(heap_len, SPEC.initial_size as usize);
309+
310+
// fill up the rest of the per-instance limit area
311+
let new_heap_area = inst
312+
.alloc_mut()
313+
.expand_heap(64 * 1024, module.as_ref())
314+
.expect("expand_heap succeeds");
315+
assert_eq!(heap_len, new_heap_area as usize);
316+
317+
let new_heap_len = inst.alloc().heap_len();
318+
assert_eq!(new_heap_len, INSTANCE_LIMIT);
319+
320+
let past_limit_heap_area = inst.alloc_mut().expand_heap(64 * 1024, module.as_ref());
321+
assert!(
322+
past_limit_heap_area.is_err(),
323+
"heap expansion past limit fails"
324+
);
325+
326+
let still_heap_len = inst.alloc().heap_len();
327+
assert_eq!(still_heap_len, INSTANCE_LIMIT);
328+
329+
let heap = unsafe { inst.alloc_mut().heap_mut() };
330+
assert_eq!(heap[new_heap_len - 1], 0);
331+
heap[new_heap_len - 1] = 0xFF;
332+
assert_eq!(heap[new_heap_len - 1], 0xFF);
333+
}
334+
286335
const INITIAL_OVERSIZE_HEAP: HeapSpec = HeapSpec {
287336
reserved_size: SPEC_HEAP_RESERVED_SIZE,
288337
guard_size: SPEC_HEAP_GUARD_SIZE,
@@ -1134,4 +1183,11 @@ macro_rules! alloc_tests {
11341183
}
11351184

11361185
#[cfg(test)]
1137-
alloc_tests!(crate::region::mmap::MmapRegion);
1186+
mod mmap {
1187+
alloc_tests!(crate::region::mmap::MmapRegion);
1188+
}
1189+
1190+
#[cfg(all(test, feature = "uffd"))]
1191+
mod uffd {
1192+
alloc_tests!(crate::region::uffd::UffdRegion);
1193+
}

lucet-runtime/lucet-runtime-internals/src/module.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ pub trait ModuleInternal: Send + Sync {
118118
));
119119
}
120120
let heap_memory_size = std::cmp::min(limits.heap_memory_size, instance_heap_limit);
121-
122121
// Modules without heap specs will not access the heap
123122
if let Some(heap) = self.heap_spec() {
124123
// Assure that the total reserved + guard regions fit in the address space.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ impl RegionInternal for MmapRegion {
8383
mut alloc_strategy: AllocStrategy,
8484
) -> Result<InstanceHandle, Error> {
8585
let limits = self.get_limits();
86+
8687
module.validate_runtime_spec(&limits, heap_memory_size_limit)?;
8788

8889
// Use the supplied alloc_strategy to get the next available slot

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

100755100644
Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use crate::{lucet_bail, lucet_ensure, lucet_format_err};
1212
use libc::c_void;
1313
use nix::poll;
1414
use nix::sys::mman::{madvise, mmap, munmap, MapFlags, MmapAdvise, ProtFlags};
15-
use std::cmp::Ordering;
1615
use std::os::unix::io::{AsRawFd, RawFd};
1716
use std::ptr;
1817
use std::sync::{Arc, Mutex, Weak};
@@ -217,30 +216,8 @@ impl RegionInternal for UffdRegion {
217216
embed_ctx: CtxMap,
218217
heap_memory_size_limit: usize,
219218
) -> Result<InstanceHandle, Error> {
220-
let custom_limits;
221-
let mut limits = self.get_limits();
222-
223-
// Affirm that the module, if instantiated, would not violate
224-
// any runtime memory limits.
225-
match heap_memory_size_limit.cmp(&limits.heap_memory_size) {
226-
Ordering::Less => {
227-
// The supplied heap_memory_size is smaller than
228-
// default. Augment the limits with this custom value
229-
// so that it may be validated.
230-
custom_limits = Limits {
231-
heap_memory_size: heap_memory_size_limit,
232-
..*limits
233-
};
234-
limits = &custom_limits;
235-
}
236-
Ordering::Equal => (),
237-
Ordering::Greater => {
238-
return Err(Error::InvalidArgument(
239-
"heap memory size requested for instance is larger than slot allows",
240-
))
241-
}
242-
}
243-
module.validate_runtime_spec(&limits)?;
219+
let limits = self.get_limits();
220+
module.validate_runtime_spec(&limits, heap_memory_size_limit)?;
244221

245222
let slot = self
246223
.freelist
@@ -249,12 +226,11 @@ impl RegionInternal for UffdRegion {
249226
.pop()
250227
.ok_or(Error::RegionFull(self.instance_capacity))?;
251228

252-
if slot.heap as usize % host_page_size() != 0 {
253-
lucet_bail!("heap is not page-aligned; this is a bug");
254-
}
255-
256-
let limits = &slot.limits;
257-
module.validate_runtime_spec(limits)?;
229+
assert_eq!(
230+
slot.heap as usize % host_page_size(),
231+
0,
232+
"heap must be page-aligned"
233+
);
258234

259235
for (ptr, len) in [
260236
// zero the globals
@@ -270,7 +246,7 @@ impl RegionInternal for UffdRegion {
270246
unsafe {
271247
self.uffd
272248
.zeropage(*ptr, *len, true)
273-
.map_err(|e| Error::InternalError(e.into()))?;
249+
.expect("uffd.zeropage succeeds");
274250
}
275251
}
276252
}
@@ -410,10 +386,6 @@ impl UffdRegion {
410386
/// This also creates and starts a separate thread that is responsible for handling page faults
411387
/// that occur within the memory region.
412388
pub fn create(instance_capacity: usize, limits: &Limits) -> Result<Arc<Self>, Error> {
413-
assert!(
414-
limits.signal_stack_size % host_page_size() == 0,
415-
"signal stack size is a multiple of host page size"
416-
);
417389
if instance_capacity == 0 {
418390
return Err(Error::InvalidArgument(
419391
"region must be able to hold at least one instance",

0 commit comments

Comments
 (0)