Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wiggle/test-helpers/src/lib.rs
1692 views
1
use proptest::prelude::*;
2
use std::marker;
3
use wiggle::GuestMemory;
4
5
#[derive(Debug, Clone)]
6
pub struct MemAreas(Vec<MemArea>);
7
impl MemAreas {
8
pub fn new() -> Self {
9
MemAreas(Vec::new())
10
}
11
pub fn insert(&mut self, a: MemArea) {
12
// Find if `a` is already in the vector
13
match self.0.binary_search(&a) {
14
// It is present - insert it next to existing one
15
Ok(loc) => self.0.insert(loc, a),
16
// It is not present - heres where to insert it
17
Err(loc) => self.0.insert(loc, a),
18
}
19
}
20
pub fn iter(&self) -> impl Iterator<Item = &MemArea> {
21
self.0.iter()
22
}
23
}
24
25
impl<R> From<R> for MemAreas
26
where
27
R: AsRef<[MemArea]>,
28
{
29
fn from(ms: R) -> MemAreas {
30
let mut out = MemAreas::new();
31
for m in ms.as_ref().into_iter() {
32
out.insert(*m);
33
}
34
out
35
}
36
}
37
38
impl From<MemAreas> for Vec<MemArea> {
39
fn from(areas: MemAreas) -> Vec<MemArea> {
40
areas.0.clone()
41
}
42
}
43
44
#[repr(align(4096))]
45
struct HostBuffer {
46
cell: [u8; 4096],
47
}
48
49
unsafe impl Send for HostBuffer {}
50
unsafe impl Sync for HostBuffer {}
51
52
pub struct HostMemory {
53
buffer: HostBuffer,
54
}
55
impl HostMemory {
56
pub fn new() -> Self {
57
HostMemory {
58
buffer: HostBuffer { cell: [0; 4096] },
59
}
60
}
61
62
pub fn guest_memory(&mut self) -> GuestMemory<'_> {
63
GuestMemory::Unshared(&mut self.buffer.cell)
64
}
65
66
pub fn base(&self) -> *const u8 {
67
self.buffer.cell.as_ptr()
68
}
69
70
pub fn mem_area_strat(align: u32) -> BoxedStrategy<MemArea> {
71
prop::num::u32::ANY
72
.prop_filter_map("needs to fit in memory", move |p| {
73
let p_aligned = p - (p % align); // Align according to argument
74
let ptr = p_aligned % 4096; // Put inside memory
75
if ptr + align < 4096 {
76
Some(MemArea { ptr, len: align })
77
} else {
78
None
79
}
80
})
81
.boxed()
82
}
83
84
/// Takes a sorted list or memareas, and gives a sorted list of memareas covering
85
/// the parts of memory not covered by the previous
86
pub fn invert(regions: &MemAreas) -> MemAreas {
87
let mut out = MemAreas::new();
88
let mut start = 0;
89
for r in regions.iter() {
90
let len = r.ptr - start;
91
if len > 0 {
92
out.insert(MemArea {
93
ptr: start,
94
len: r.ptr - start,
95
});
96
}
97
start = r.ptr + r.len;
98
}
99
if start < 4096 {
100
out.insert(MemArea {
101
ptr: start,
102
len: 4096 - start,
103
});
104
}
105
out
106
}
107
108
pub fn byte_slice_strat(size: u32, align: u32, exclude: &MemAreas) -> BoxedStrategy<MemArea> {
109
let available: Vec<MemArea> = Self::invert(exclude)
110
.iter()
111
.flat_map(|a| a.inside(size))
112
.filter(|a| a.ptr % align == 0)
113
.collect();
114
115
Just(available)
116
.prop_filter("available memory for allocation", |a| !a.is_empty())
117
.prop_flat_map(|a| prop::sample::select(a))
118
.boxed()
119
}
120
}
121
122
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
123
pub struct MemArea {
124
pub ptr: u32,
125
pub len: u32,
126
}
127
128
impl MemArea {
129
// This code is a whole lot like the Region::overlaps func that's at the core of the code under
130
// test.
131
// So, I implemented this one with std::ops::Range so it is less likely I wrote the same bug in two
132
// places.
133
pub fn overlapping(&self, b: Self) -> bool {
134
// a_range is all elems in A
135
let a_range = std::ops::Range {
136
start: self.ptr,
137
end: self.ptr + self.len, // std::ops::Range is open from the right
138
};
139
// b_range is all elems in B
140
let b_range = std::ops::Range {
141
start: b.ptr,
142
end: b.ptr + b.len,
143
};
144
// No element in B is contained in A:
145
for b_elem in b_range.clone() {
146
if a_range.contains(&b_elem) {
147
return true;
148
}
149
}
150
// No element in A is contained in B:
151
for a_elem in a_range {
152
if b_range.contains(&a_elem) {
153
return true;
154
}
155
}
156
return false;
157
}
158
pub fn non_overlapping_set<M>(areas: M) -> bool
159
where
160
M: Into<MemAreas>,
161
{
162
let areas = areas.into();
163
for (aix, a) in areas.iter().enumerate() {
164
for (bix, b) in areas.iter().enumerate() {
165
if aix != bix {
166
// (A, B) is every pairing of areas
167
if a.overlapping(*b) {
168
return false;
169
}
170
}
171
}
172
}
173
return true;
174
}
175
176
/// Enumerate all memareas of size `len` inside a given area
177
fn inside(&self, len: u32) -> impl Iterator<Item = MemArea> + use<> {
178
let end: i64 = self.len as i64 - len as i64;
179
let start = self.ptr;
180
(0..end).map(move |v| MemArea {
181
ptr: start + v as u32,
182
len,
183
})
184
}
185
}
186
187
#[cfg(test)]
188
mod test {
189
use super::*;
190
191
#[test]
192
fn hostmemory_is_aligned() {
193
let h = HostMemory::new();
194
assert_eq!(h.base() as usize % 4096, 0);
195
let h = Box::new(h);
196
assert_eq!(h.base() as usize % 4096, 0);
197
}
198
199
#[test]
200
fn invert() {
201
fn invert_equality(input: &[MemArea], expected: &[MemArea]) {
202
let input: MemAreas = input.into();
203
let inverted: Vec<MemArea> = HostMemory::invert(&input).into();
204
assert_eq!(expected, inverted.as_slice());
205
}
206
207
invert_equality(&[], &[MemArea { ptr: 0, len: 4096 }]);
208
invert_equality(
209
&[MemArea { ptr: 0, len: 1 }],
210
&[MemArea { ptr: 1, len: 4095 }],
211
);
212
213
invert_equality(
214
&[MemArea { ptr: 1, len: 1 }],
215
&[MemArea { ptr: 0, len: 1 }, MemArea { ptr: 2, len: 4094 }],
216
);
217
218
invert_equality(
219
&[MemArea { ptr: 1, len: 4095 }],
220
&[MemArea { ptr: 0, len: 1 }],
221
);
222
223
invert_equality(
224
&[MemArea { ptr: 0, len: 1 }, MemArea { ptr: 1, len: 4095 }],
225
&[],
226
);
227
228
invert_equality(
229
&[MemArea { ptr: 1, len: 2 }, MemArea { ptr: 4, len: 1 }],
230
&[
231
MemArea { ptr: 0, len: 1 },
232
MemArea { ptr: 3, len: 1 },
233
MemArea { ptr: 5, len: 4091 },
234
],
235
);
236
}
237
238
fn set_of_slices_strat(
239
s1: u32,
240
s2: u32,
241
s3: u32,
242
) -> BoxedStrategy<(MemArea, MemArea, MemArea)> {
243
HostMemory::byte_slice_strat(s1, 1, &MemAreas::new())
244
.prop_flat_map(move |a1| {
245
(
246
Just(a1),
247
HostMemory::byte_slice_strat(s2, 1, &MemAreas::from(&[a1])),
248
)
249
})
250
.prop_flat_map(move |(a1, a2)| {
251
(
252
Just(a1),
253
Just(a2),
254
HostMemory::byte_slice_strat(s3, 1, &MemAreas::from(&[a1, a2])),
255
)
256
})
257
.boxed()
258
}
259
260
#[test]
261
fn trivial_inside() {
262
let a = MemArea { ptr: 24, len: 4072 };
263
let interior = a.inside(24).collect::<Vec<_>>();
264
265
assert!(interior.len() > 0);
266
}
267
268
proptest! {
269
#[test]
270
// For some random region of decent size
271
fn inside(r in HostMemory::mem_area_strat(123)) {
272
let set_of_r = MemAreas::from(&[r]);
273
// All regions outside of r:
274
let exterior = HostMemory::invert(&set_of_r);
275
// All regions inside of r:
276
let interior = r.inside(22);
277
for i in interior {
278
// i overlaps with r:
279
assert!(r.overlapping(i));
280
// i is inside r:
281
assert!(i.ptr >= r.ptr);
282
assert!(r.ptr + r.len >= i.ptr + i.len);
283
// the set of exterior and i is non-overlapping
284
let mut all = exterior.clone();
285
all.insert(i);
286
assert!(MemArea::non_overlapping_set(all));
287
}
288
}
289
290
#[test]
291
fn byte_slices((s1, s2, s3) in set_of_slices_strat(12, 34, 56)) {
292
let all = MemAreas::from(&[s1, s2, s3]);
293
assert!(MemArea::non_overlapping_set(all));
294
}
295
}
296
}
297
298
use std::cell::RefCell;
299
use wiggle::GuestError;
300
301
// In lucet, our Ctx struct needs a lifetime, so we're using one
302
// on the test as well.
303
pub struct WasiCtx<'a> {
304
pub guest_errors: RefCell<Vec<GuestError>>,
305
pub log: RefCell<Vec<String>>,
306
lifetime: marker::PhantomData<&'a ()>,
307
}
308
309
impl<'a> WasiCtx<'a> {
310
pub fn new() -> Self {
311
Self {
312
guest_errors: RefCell::new(vec![]),
313
log: RefCell::new(vec![]),
314
lifetime: marker::PhantomData,
315
}
316
}
317
}
318
319
// Errno is used as a first return value in the functions above, therefore
320
// it must implement GuestErrorType with type Context = WasiCtx.
321
// The context type should let you do logging or debugging or whatever you need
322
// with these errors. We just push them to vecs.
323
#[macro_export]
324
macro_rules! impl_errno {
325
( $errno:ty ) => {
326
impl wiggle::GuestErrorType for $errno {
327
fn success() -> $errno {
328
<$errno>::Ok
329
}
330
}
331
};
332
}
333
334