Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/fuzzing/src/generators/memory.rs
1693 views
1
//! Generate various kinds of Wasm memory.
2
3
use arbitrary::{Arbitrary, Unstructured};
4
5
/// A description of a memory config, image, etc... that can be used to test
6
/// memory accesses.
7
#[derive(Debug)]
8
pub struct MemoryAccesses {
9
/// The configuration to use with this test case.
10
pub config: crate::generators::Config,
11
/// The heap image to use with this test case.
12
pub image: HeapImage,
13
/// The offset immediate to encode in the `load{8,16,32,64}` functions'
14
/// various load instructions.
15
pub offset: u32,
16
/// The amount (in pages) to grow the memory.
17
pub growth: u32,
18
}
19
20
impl<'a> Arbitrary<'a> for MemoryAccesses {
21
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
22
let image = HeapImage::arbitrary(u)?;
23
24
// Don't grow too much, since oss-fuzz/asan get upset if we try,
25
// even if we allow it to fail.
26
let one_mib = 1 << 20; // 1 MiB
27
let max_growth = one_mib / (1 << image.page_size_log2.unwrap_or(16));
28
let mut growth: u32 = u.int_in_range(0..=max_growth)?;
29
30
// Occasionally, round to a power of two, since these tend to be
31
// interesting numbers that overlap with the host page size and things
32
// like that.
33
if growth > 0 && u.ratio(1, 20)? {
34
growth = (growth - 1).next_power_of_two();
35
}
36
37
Ok(MemoryAccesses {
38
config: u.arbitrary()?,
39
image,
40
offset: u.arbitrary()?,
41
growth,
42
})
43
}
44
}
45
46
/// A memory heap image.
47
pub struct HeapImage {
48
/// The minimum size (in pages) of this memory.
49
pub minimum: u32,
50
/// The maximum size (in pages) of this memory.
51
pub maximum: Option<u32>,
52
/// Whether this memory should be indexed with `i64` (rather than `i32`).
53
pub memory64: bool,
54
/// The log2 of the page size for this memory.
55
pub page_size_log2: Option<u32>,
56
/// Data segments for this memory.
57
pub segments: Vec<(u32, Vec<u8>)>,
58
}
59
60
impl std::fmt::Debug for HeapImage {
61
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62
struct Segments<'a>(&'a [(u32, Vec<u8>)]);
63
impl std::fmt::Debug for Segments<'_> {
64
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65
write!(f, "[..; {}]", self.0.len())
66
}
67
}
68
69
f.debug_struct("HeapImage")
70
.field("minimum", &self.minimum)
71
.field("maximum", &self.maximum)
72
.field("memory64", &self.memory64)
73
.field("page_size_log2", &self.page_size_log2)
74
.field("segments", &Segments(&self.segments))
75
.finish()
76
}
77
}
78
79
impl<'a> Arbitrary<'a> for HeapImage {
80
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
81
let minimum = u.int_in_range(0..=4)?;
82
let maximum = if u.arbitrary()? {
83
Some(u.int_in_range(minimum..=10)?)
84
} else {
85
None
86
};
87
let memory64 = u.arbitrary()?;
88
let page_size_log2 = match u.int_in_range(0..=2)? {
89
0 => None,
90
1 => Some(0),
91
2 => Some(16),
92
_ => unreachable!(),
93
};
94
let mut segments = vec![];
95
if minimum > 0 {
96
for _ in 0..u.int_in_range(0..=4)? {
97
let last_addressable = (1u32 << page_size_log2.unwrap_or(16)) * minimum - 1;
98
let offset = u.int_in_range(0..=last_addressable)?;
99
let max_len =
100
std::cmp::min(u.len(), usize::try_from(last_addressable - offset).unwrap());
101
let len = u.int_in_range(0..=max_len)?;
102
let data = u.bytes(len)?.to_vec();
103
segments.push((offset, data));
104
}
105
}
106
Ok(HeapImage {
107
minimum,
108
maximum,
109
memory64,
110
page_size_log2,
111
segments,
112
})
113
}
114
}
115
116
/// Represents a normal memory configuration for Wasmtime with the given
117
/// static and dynamic memory sizes.
118
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
119
#[expect(missing_docs, reason = "self-describing fields")]
120
pub struct MemoryConfig {
121
pub memory_reservation: Option<u64>,
122
pub memory_guard_size: Option<u64>,
123
pub memory_reservation_for_growth: Option<u64>,
124
pub guard_before_linear_memory: bool,
125
pub cranelift_enable_heap_access_spectre_mitigations: Option<bool>,
126
pub memory_init_cow: bool,
127
}
128
129
impl<'a> Arbitrary<'a> for MemoryConfig {
130
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
131
Ok(Self {
132
// Allow up to 8GiB reservations of the virtual address space for
133
// the initial memory reservation.
134
memory_reservation: interesting_virtual_memory_size(u, 33)?,
135
136
// Allow up to 4GiB guard page reservations to be made.
137
memory_guard_size: interesting_virtual_memory_size(u, 32)?,
138
139
// Allow up up to 1GiB extra memory to grow into for dynamic
140
// memories.
141
memory_reservation_for_growth: interesting_virtual_memory_size(u, 30)?,
142
143
guard_before_linear_memory: u.arbitrary()?,
144
cranelift_enable_heap_access_spectre_mitigations: u.arbitrary()?,
145
memory_init_cow: u.arbitrary()?,
146
})
147
}
148
}
149
150
/// Helper function to generate "interesting numbers" for virtual memory
151
/// configuration options that `Config` supports.
152
fn interesting_virtual_memory_size(
153
u: &mut Unstructured<'_>,
154
max_log2: u32,
155
) -> arbitrary::Result<Option<u64>> {
156
// Most of the time return "none" meaning "use the default settings".
157
if u.ratio(3, 4)? {
158
return Ok(None);
159
}
160
161
// Otherwise do a split between various strategies.
162
#[derive(Arbitrary)]
163
enum Interesting {
164
Zero,
165
PowerOfTwo,
166
Arbitrary,
167
}
168
169
let size = match u.arbitrary()? {
170
Interesting::Zero => 0,
171
Interesting::PowerOfTwo => 1 << u.int_in_range(0..=max_log2)?,
172
Interesting::Arbitrary => u.int_in_range(0..=1 << max_log2)?,
173
};
174
Ok(Some(size))
175
}
176
177
impl MemoryConfig {
178
/// Apply this memory configuration to the given config.
179
pub fn configure(&self, cfg: &mut wasmtime_cli_flags::CommonOptions) {
180
cfg.opts.memory_reservation = self.memory_reservation;
181
cfg.opts.memory_guard_size = self.memory_guard_size;
182
cfg.opts.memory_reservation_for_growth = self.memory_reservation_for_growth;
183
cfg.opts.guard_before_linear_memory = Some(self.guard_before_linear_memory);
184
cfg.opts.memory_init_cow = Some(self.memory_init_cow);
185
186
if let Some(enable) = self.cranelift_enable_heap_access_spectre_mitigations {
187
cfg.codegen.cranelift.push((
188
"enable_heap_access_spectre_mitigation".to_string(),
189
Some(enable.to_string()),
190
));
191
}
192
}
193
}
194
195