1 use proptest::prelude::*;
2 use wiggle::{GuestMemory, GuestPtr};
3 use wiggle_test::{HostMemory, MemArea, MemAreas, WasiCtx, impl_errno};
4
5 wiggle::from_witx!({
6 witx: ["tests/strings.witx"],
7 });
8
9 impl_errno!(types::Errno);
10
11 impl<'a> strings::Strings for WasiCtx<'a> {
hello_string( &mut self, memory: &mut GuestMemory<'_>, a_string: GuestPtr<str>, ) -> Result<u32, types::Errno>12 fn hello_string(
13 &mut self,
14 memory: &mut GuestMemory<'_>,
15 a_string: GuestPtr<str>,
16 ) -> Result<u32, types::Errno> {
17 let s = memory
18 .as_str(a_string)
19 .expect("should be valid string")
20 .expect("expected non-shared memory");
21 println!("a_string='{}'", &*s);
22 Ok(s.len() as u32)
23 }
24
multi_string( &mut self, memory: &mut GuestMemory<'_>, a: GuestPtr<str>, b: GuestPtr<str>, c: GuestPtr<str>, ) -> Result<u32, types::Errno>25 fn multi_string(
26 &mut self,
27 memory: &mut GuestMemory<'_>,
28 a: GuestPtr<str>,
29 b: GuestPtr<str>,
30 c: GuestPtr<str>,
31 ) -> Result<u32, types::Errno> {
32 let sa = memory
33 .as_str(a)
34 .expect("A should be valid string")
35 .expect("expected non-shared memory");
36 let sb = memory
37 .as_str(b)
38 .expect("B should be valid string")
39 .expect("expected non-shared memory");
40 let sc = memory
41 .as_str(c)
42 .expect("C should be valid string")
43 .expect("expected non-shared memory");
44 let total_len = sa.len() + sb.len() + sc.len();
45 println!(
46 "len={}, a='{}', b='{}', c='{}'",
47 total_len, &*sa, &*sb, &*sc
48 );
49 Ok(total_len as u32)
50 }
51 }
52
unicode_string_strategy() -> impl Strategy<Value = String>53 fn unicode_string_strategy() -> impl Strategy<Value = String> {
54 "\\p{Greek}{1,256}"
55 }
ascii_string_strategy() -> impl Strategy<Value = String>56 fn ascii_string_strategy() -> impl Strategy<Value = String> {
57 "[a-zA-Z0..9]{1,256}"
58 }
59
60 #[derive(Debug)]
61 struct HelloStringExercise {
62 test_word: String,
63 string_ptr_loc: MemArea,
64 return_ptr_loc: MemArea,
65 }
66
67 impl HelloStringExercise {
strat() -> BoxedStrategy<Self>68 pub fn strat() -> BoxedStrategy<Self> {
69 (unicode_string_strategy(),)
70 .prop_flat_map(|(test_word,)| {
71 (
72 Just(test_word.clone()),
73 HostMemory::mem_area_strat(test_word.len() as u32),
74 HostMemory::mem_area_strat(4),
75 )
76 })
77 .prop_map(|(test_word, string_ptr_loc, return_ptr_loc)| Self {
78 test_word,
79 string_ptr_loc,
80 return_ptr_loc,
81 })
82 .prop_filter("non-overlapping pointers", |e| {
83 MemArea::non_overlapping_set(&[e.string_ptr_loc, e.return_ptr_loc])
84 })
85 .boxed()
86 }
87
test(&self)88 pub fn test(&self) {
89 let mut ctx = WasiCtx::new();
90 let mut host_memory = HostMemory::new();
91 let mut memory = host_memory.guest_memory();
92
93 // Populate string in guest's memory
94 let ptr = GuestPtr::<str>::new((self.string_ptr_loc.ptr, self.test_word.len() as u32));
95 for (slot, byte) in ptr.as_bytes().iter().zip(self.test_word.bytes()) {
96 memory
97 .write(slot.expect("should be valid pointer"), byte)
98 .expect("failed to write");
99 }
100
101 let res = strings::hello_string(
102 &mut ctx,
103 &mut memory,
104 self.string_ptr_loc.ptr as i32,
105 self.test_word.len() as i32,
106 self.return_ptr_loc.ptr as i32,
107 )
108 .unwrap();
109 assert_eq!(res, types::Errno::Ok as i32, "hello string errno");
110
111 let given = memory
112 .read(GuestPtr::<u32>::new(self.return_ptr_loc.ptr))
113 .expect("deref ptr to return value");
114 assert_eq!(self.test_word.len() as u32, given);
115 }
116 }
117 proptest! {
118 #[test]
119 fn hello_string(e in HelloStringExercise::strat()) {
120 e.test()
121 }
122 }
123
124 #[derive(Debug)]
125 struct MultiStringExercise {
126 a: String,
127 b: String,
128 c: String,
129 sa_ptr_loc: MemArea,
130 sb_ptr_loc: MemArea,
131 sc_ptr_loc: MemArea,
132 return_ptr_loc: MemArea,
133 }
134
135 impl MultiStringExercise {
strat() -> BoxedStrategy<Self>136 pub fn strat() -> BoxedStrategy<Self> {
137 (
138 unicode_string_strategy(),
139 unicode_string_strategy(),
140 unicode_string_strategy(),
141 HostMemory::mem_area_strat(4),
142 )
143 .prop_flat_map(|(a, b, c, return_ptr_loc)| {
144 (
145 Just(a.clone()),
146 Just(b.clone()),
147 Just(c.clone()),
148 HostMemory::byte_slice_strat(
149 a.len() as u32,
150 1,
151 &MemAreas::from([return_ptr_loc]),
152 ),
153 Just(return_ptr_loc),
154 )
155 })
156 .prop_flat_map(|(a, b, c, sa_ptr_loc, return_ptr_loc)| {
157 (
158 Just(a.clone()),
159 Just(b.clone()),
160 Just(c.clone()),
161 Just(sa_ptr_loc),
162 HostMemory::byte_slice_strat(
163 b.len() as u32,
164 1,
165 &MemAreas::from([sa_ptr_loc, return_ptr_loc]),
166 ),
167 Just(return_ptr_loc),
168 )
169 })
170 .prop_flat_map(|(a, b, c, sa_ptr_loc, sb_ptr_loc, return_ptr_loc)| {
171 (
172 Just(a.clone()),
173 Just(b.clone()),
174 Just(c.clone()),
175 Just(sa_ptr_loc),
176 Just(sb_ptr_loc),
177 HostMemory::byte_slice_strat(
178 c.len() as u32,
179 1,
180 &MemAreas::from([sa_ptr_loc, sb_ptr_loc, return_ptr_loc]),
181 ),
182 Just(return_ptr_loc),
183 )
184 })
185 .prop_map(
186 |(a, b, c, sa_ptr_loc, sb_ptr_loc, sc_ptr_loc, return_ptr_loc)| {
187 MultiStringExercise {
188 a,
189 b,
190 c,
191 sa_ptr_loc,
192 sb_ptr_loc,
193 sc_ptr_loc,
194 return_ptr_loc,
195 }
196 },
197 )
198 .boxed()
199 }
200
test(&self)201 pub fn test(&self) {
202 let mut ctx = WasiCtx::new();
203 let mut host_memory = HostMemory::new();
204 let mut memory = host_memory.guest_memory();
205
206 let mut write_string = |val: &str, loc: MemArea| {
207 let ptr = GuestPtr::<str>::new((loc.ptr, val.len() as u32));
208 for (slot, byte) in ptr.as_bytes().iter().zip(val.bytes()) {
209 memory
210 .write(slot.expect("should be valid pointer"), byte)
211 .expect("failed to write");
212 }
213 };
214
215 write_string(&self.a, self.sa_ptr_loc);
216 write_string(&self.b, self.sb_ptr_loc);
217 write_string(&self.c, self.sc_ptr_loc);
218
219 let res = strings::multi_string(
220 &mut ctx,
221 &mut memory,
222 self.sa_ptr_loc.ptr as i32,
223 self.a.len() as i32,
224 self.sb_ptr_loc.ptr as i32,
225 self.b.len() as i32,
226 self.sc_ptr_loc.ptr as i32,
227 self.c.len() as i32,
228 self.return_ptr_loc.ptr as i32,
229 )
230 .unwrap();
231 assert_eq!(res, types::Errno::Ok as i32, "multi string errno");
232
233 let given = memory
234 .read(GuestPtr::<u32>::new(self.return_ptr_loc.ptr))
235 .expect("deref ptr to return value");
236 assert_eq!((self.a.len() + self.b.len() + self.c.len()) as u32, given);
237 }
238 }
239 proptest! {
240 #[test]
241 fn multi_string(e in MultiStringExercise::strat()) {
242 e.test()
243 }
244 }
245
246 #[derive(Debug)]
247 struct OverlappingStringExercise {
248 a: String,
249 sa_ptr_loc: MemArea,
250 offset_b: u32,
251 offset_c: u32,
252 return_ptr_loc: MemArea,
253 }
254
255 impl OverlappingStringExercise {
strat() -> BoxedStrategy<Self>256 pub fn strat() -> BoxedStrategy<Self> {
257 // using ascii so we can window into it without worrying about codepoints
258 (ascii_string_strategy(), HostMemory::mem_area_strat(4))
259 .prop_flat_map(|(a, return_ptr_loc)| {
260 (
261 Just(a.clone()),
262 HostMemory::mem_area_strat(a.len() as u32),
263 0..(a.len() as u32),
264 0..(a.len() as u32),
265 Just(return_ptr_loc),
266 )
267 })
268 .prop_map(|(a, sa_ptr_loc, offset_b, offset_c, return_ptr_loc)| Self {
269 a,
270 sa_ptr_loc,
271 offset_b,
272 offset_c,
273 return_ptr_loc,
274 })
275 .prop_filter("non-overlapping pointers", |e| {
276 MemArea::non_overlapping_set(&[e.sa_ptr_loc, e.return_ptr_loc])
277 })
278 .boxed()
279 }
280
test(&self)281 pub fn test(&self) {
282 let mut ctx = WasiCtx::new();
283 let mut host_memory = HostMemory::new();
284 let mut memory = host_memory.guest_memory();
285
286 let mut write_string = |val: &str, loc: MemArea| {
287 let ptr = GuestPtr::<str>::new((loc.ptr, val.len() as u32));
288 for (slot, byte) in ptr.as_bytes().iter().zip(val.bytes()) {
289 memory
290 .write(slot.expect("should be valid pointer"), byte)
291 .expect("failed to write");
292 }
293 };
294
295 write_string(&self.a, self.sa_ptr_loc);
296
297 let a_len = self.a.as_bytes().len() as i32;
298 let res = strings::multi_string(
299 &mut ctx,
300 &mut memory,
301 self.sa_ptr_loc.ptr as i32,
302 a_len,
303 (self.sa_ptr_loc.ptr + self.offset_b) as i32,
304 a_len - self.offset_b as i32,
305 (self.sa_ptr_loc.ptr + self.offset_c) as i32,
306 a_len - self.offset_c as i32,
307 self.return_ptr_loc.ptr as i32,
308 )
309 .unwrap();
310 assert_eq!(res, types::Errno::Ok as i32, "multi string errno");
311
312 let given = memory
313 .read(GuestPtr::<u32>::new(self.return_ptr_loc.ptr))
314 .expect("deref ptr to return value");
315 assert_eq!(
316 ((3 * a_len) - (self.offset_b as i32 + self.offset_c as i32)) as u32,
317 given
318 );
319 }
320 }
321
322 proptest! {
323 #[test]
324 fn overlapping_string(e in OverlappingStringExercise::strat()) {
325 e.test()
326 }
327 }
328