wasmtime_fuzzing/generators/
memory.rs1use arbitrary::{Arbitrary, Unstructured};
4
5#[derive(Debug)]
8pub struct MemoryAccesses {
9 pub config: crate::generators::Config,
11 pub image: HeapImage,
13 pub offset: u32,
16 pub growth: u32,
18}
19
20impl<'a> Arbitrary<'a> for MemoryAccesses {
21 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
22 let image = HeapImage::arbitrary(u)?;
23
24 let one_mib = 1 << 20; 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 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
46pub struct HeapImage {
48 pub minimum: u32,
50 pub maximum: Option<u32>,
52 pub memory64: bool,
54 pub page_size_log2: Option<u32>,
56 pub segments: Vec<(u32, Vec<u8>)>,
58}
59
60impl 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
79impl<'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#[derive(Clone, Debug, Eq, Hash, PartialEq)]
119#[expect(missing_docs, reason = "self-describing fields")]
120pub 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 pub gc_heap_reservation: Option<u64>,
128 pub gc_heap_guard_size: Option<u64>,
129 pub gc_heap_reservation_for_growth: Option<u64>,
130 pub gc_heap_may_move: Option<bool>,
131 pub gc_heap_initial_size: Option<u64>,
132}
133
134impl<'a> Arbitrary<'a> for MemoryConfig {
135 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
136 let max_log2 = if cfg!(target_pointer_width = "32") {
137 25
138 } else {
139 63
140 };
141 Ok(Self {
142 memory_reservation: interesting_virtual_memory_size(u, 33.min(max_log2))?,
145
146 memory_guard_size: interesting_virtual_memory_size(u, 32.min(max_log2))?,
148
149 memory_reservation_for_growth: interesting_virtual_memory_size(u, 30.min(max_log2))?,
151
152 guard_before_linear_memory: u.arbitrary()?,
153 cranelift_enable_heap_access_spectre_mitigations: u.arbitrary()?,
154 memory_init_cow: u.arbitrary()?,
155
156 gc_heap_reservation: interesting_virtual_memory_size(u, 33.min(max_log2))?,
158 gc_heap_guard_size: interesting_virtual_memory_size(u, 32.min(max_log2))?,
159 gc_heap_reservation_for_growth: interesting_virtual_memory_size(u, 30.min(max_log2))?,
160 gc_heap_may_move: u.arbitrary()?,
161 gc_heap_initial_size: interesting_virtual_memory_size(u, 22.min(max_log2))?,
162 })
163 }
164}
165
166fn interesting_virtual_memory_size(
169 u: &mut Unstructured<'_>,
170 max_log2: u32,
171) -> arbitrary::Result<Option<u64>> {
172 if u.ratio(3, 4)? {
174 return Ok(None);
175 }
176
177 #[derive(Arbitrary)]
179 enum Interesting {
180 Zero,
181 PowerOfTwo,
182 Arbitrary,
183 }
184
185 let size = match u.arbitrary()? {
186 Interesting::Zero => 0,
187 Interesting::PowerOfTwo => 1 << u.int_in_range(0..=max_log2)?,
188 Interesting::Arbitrary => u.int_in_range(0..=1 << max_log2)?,
189 };
190 Ok(Some(size))
191}
192
193impl MemoryConfig {
194 pub fn configure(&self, cfg: &mut wasmtime_cli_flags::CommonOptions) {
196 cfg.opts.memory_reservation = self.memory_reservation;
197 cfg.opts.memory_guard_size = self.memory_guard_size;
198 cfg.opts.memory_reservation_for_growth = self.memory_reservation_for_growth;
199 cfg.opts.guard_before_linear_memory = Some(self.guard_before_linear_memory);
200 cfg.opts.memory_init_cow = Some(self.memory_init_cow);
201 cfg.opts.gc_heap_reservation = self.gc_heap_reservation;
202 cfg.opts.gc_heap_guard_size = self.gc_heap_guard_size;
203 cfg.opts.gc_heap_reservation_for_growth = self.gc_heap_reservation_for_growth;
204 cfg.opts.gc_heap_may_move = self.gc_heap_may_move;
205 cfg.opts.gc_heap_initial_size = self.gc_heap_initial_size;
206
207 if let Some(enable) = self.cranelift_enable_heap_access_spectre_mitigations {
208 cfg.codegen.cranelift.push((
209 "enable_heap_access_spectre_mitigation".to_string(),
210 Some(enable.to_string()),
211 ));
212 }
213 }
214}