wasmtime_fuzzing/generators/
memory.rs1use anyhow::Result;
4use arbitrary::{Arbitrary, Unstructured};
5
6#[derive(Debug)]
9pub struct MemoryAccesses {
10 pub config: crate::generators::Config,
12 pub image: HeapImage,
14 pub offset: u32,
17 pub growth: u32,
19}
20
21impl<'a> Arbitrary<'a> for MemoryAccesses {
22 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
23 let image = HeapImage::arbitrary(u)?;
24
25 let one_mib = 1 << 20; let max_growth = one_mib / (1 << image.page_size_log2.unwrap_or(16));
29 let mut growth: u32 = u.int_in_range(0..=max_growth)?;
30
31 if growth > 0 && u.ratio(1, 20)? {
35 growth = (growth - 1).next_power_of_two();
36 }
37
38 Ok(MemoryAccesses {
39 config: u.arbitrary()?,
40 image,
41 offset: u.arbitrary()?,
42 growth,
43 })
44 }
45}
46
47pub struct HeapImage {
49 pub minimum: u32,
51 pub maximum: Option<u32>,
53 pub memory64: bool,
55 pub page_size_log2: Option<u32>,
57 pub segments: Vec<(u32, Vec<u8>)>,
59}
60
61impl std::fmt::Debug for HeapImage {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 struct Segments<'a>(&'a [(u32, Vec<u8>)]);
64 impl std::fmt::Debug for Segments<'_> {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 write!(f, "[..; {}]", self.0.len())
67 }
68 }
69
70 f.debug_struct("HeapImage")
71 .field("minimum", &self.minimum)
72 .field("maximum", &self.maximum)
73 .field("memory64", &self.memory64)
74 .field("page_size_log2", &self.page_size_log2)
75 .field("segments", &Segments(&self.segments))
76 .finish()
77 }
78}
79
80impl<'a> Arbitrary<'a> for HeapImage {
81 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
82 let minimum = u.int_in_range(0..=4)?;
83 let maximum = if u.arbitrary()? {
84 Some(u.int_in_range(minimum..=10)?)
85 } else {
86 None
87 };
88 let memory64 = u.arbitrary()?;
89 let page_size_log2 = match u.int_in_range(0..=2)? {
90 0 => None,
91 1 => Some(0),
92 2 => Some(16),
93 _ => unreachable!(),
94 };
95 let mut segments = vec![];
96 if minimum > 0 {
97 for _ in 0..u.int_in_range(0..=4)? {
98 let last_addressable = (1u32 << page_size_log2.unwrap_or(16)) * minimum - 1;
99 let offset = u.int_in_range(0..=last_addressable)?;
100 let max_len =
101 std::cmp::min(u.len(), usize::try_from(last_addressable - offset).unwrap());
102 let len = u.int_in_range(0..=max_len)?;
103 let data = u.bytes(len)?.to_vec();
104 segments.push((offset, data));
105 }
106 }
107 Ok(HeapImage {
108 minimum,
109 maximum,
110 memory64,
111 page_size_log2,
112 segments,
113 })
114 }
115}
116
117#[derive(Clone, Debug, Eq, Hash, PartialEq)]
120#[expect(missing_docs, reason = "self-describing fields")]
121pub struct MemoryConfig {
122 pub memory_reservation: Option<u64>,
123 pub memory_guard_size: Option<u64>,
124 pub memory_reservation_for_growth: Option<u64>,
125 pub guard_before_linear_memory: bool,
126 pub cranelift_enable_heap_access_spectre_mitigations: Option<bool>,
127 pub memory_init_cow: bool,
128}
129
130impl<'a> Arbitrary<'a> for MemoryConfig {
131 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
132 Ok(Self {
133 memory_reservation: interesting_virtual_memory_size(u, 33)?,
136
137 memory_guard_size: interesting_virtual_memory_size(u, 32)?,
139
140 memory_reservation_for_growth: interesting_virtual_memory_size(u, 30)?,
143
144 guard_before_linear_memory: u.arbitrary()?,
145 cranelift_enable_heap_access_spectre_mitigations: u.arbitrary()?,
146 memory_init_cow: u.arbitrary()?,
147 })
148 }
149}
150
151fn interesting_virtual_memory_size(
154 u: &mut Unstructured<'_>,
155 max_log2: u32,
156) -> arbitrary::Result<Option<u64>> {
157 if u.ratio(3, 4)? {
159 return Ok(None);
160 }
161
162 #[derive(Arbitrary)]
164 enum Interesting {
165 Zero,
166 PowerOfTwo,
167 Arbitrary,
168 }
169
170 let size = match u.arbitrary()? {
171 Interesting::Zero => 0,
172 Interesting::PowerOfTwo => 1 << u.int_in_range(0..=max_log2)?,
173 Interesting::Arbitrary => u.int_in_range(0..=1 << max_log2)?,
174 };
175 Ok(Some(size))
176}
177
178impl MemoryConfig {
179 pub fn configure(&self, cfg: &mut wasmtime_cli_flags::CommonOptions) {
181 cfg.opts.memory_reservation = self.memory_reservation;
182 cfg.opts.memory_guard_size = self.memory_guard_size;
183 cfg.opts.memory_reservation_for_growth = self.memory_reservation_for_growth;
184 cfg.opts.guard_before_linear_memory = Some(self.guard_before_linear_memory);
185 cfg.opts.memory_init_cow = Some(self.memory_init_cow);
186
187 if let Some(enable) = self.cranelift_enable_heap_access_spectre_mitigations {
188 cfg.codegen.cranelift.push((
189 "enable_heap_access_spectre_mitigation".to_string(),
190 Some(enable.to_string()),
191 ));
192 }
193 }
194}