Skip to content

Commit

Permalink
Fix an timeout in fuzzing (#9475)
Browse files Browse the repository at this point in the history
This commit fixes a timeout that was found by OSS-Fuzz recently where a
module was calling `memory.grow` many times, presumably in a loop, with
a modest amount each time. This meant that `memory.grow` was taking, in
total, a quadratic amount of time because Wasmtime was configured with
dynamic memories and no memory was reserved for growth. That in turn
meant that the test case eventually timed out due to this quadratic
behavior.

To fix this in addition to the memory allocation cap that we already
track a new cap for the number of times memories/tables can be grown was
also added. Any growth beyond this limit is rejected and helps prevent
this quadratic behavior.
  • Loading branch information
alexcrichton authored Oct 16, 2024
1 parent b62651f commit 8d32008
Showing 1 changed file with 20 additions and 0 deletions.
20 changes: 20 additions & 0 deletions crates/fuzzing/src/oracles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ pub struct StoreLimits(Arc<LimitsState>);
struct LimitsState {
/// Remaining memory, in bytes, left to allocate
remaining_memory: AtomicUsize,
/// Remaining times memories/tables can be grown
remaining_growths: AtomicUsize,
/// Whether or not an allocation request has been denied
oom: AtomicBool,
}
Expand All @@ -81,12 +83,30 @@ impl StoreLimits {
// Limits tables/memories within a store to at most 1gb for now to
// exercise some larger address but not overflow various limits.
remaining_memory: AtomicUsize::new(1 << 30),
// Also limit the number of times a memory or table may be grown.
// Otherwise infinite growths can exhibit quadratic behavior. For
// example Wasmtime could be configured with dynamic memories and no
// guard regions to grow into, meaning each memory growth could be a
// `memcpy`. As more data is added over time growths get more and
// more expensive meaning that fuel may not be effective at limiting
// execution time.
remaining_growths: AtomicUsize::new(100),
oom: AtomicBool::new(false),
}))
}

fn alloc(&mut self, amt: usize) -> bool {
log::trace!("alloc {amt:#x} bytes");
if self
.0
.remaining_growths
.fetch_update(SeqCst, SeqCst, |remaining| remaining.checked_sub(1))
.is_err()
{
self.0.oom.store(true, SeqCst);
log::debug!("too many growths, rejecting allocation");
return false;
}
match self
.0
.remaining_memory
Expand Down

0 comments on commit 8d32008

Please sign in to comment.