1 mod test_helpers;
2 
3 use crate::test_helpers::{
4     lucet_wasi_tests_internal_ensure_linked, run, run_with_null_stdin, run_with_stdout,
5     LUCET_WASI_ROOT,
6 };
7 use lucet_wasi::{WasiCtx, WasiCtxBuilder};
8 use std::fs::File;
9 use std::path::Path;
10 use tempfile::TempDir;
11 
12 #[test]
double_import()13 fn double_import() {
14     lucet_wasi_tests_internal_ensure_linked();
15 
16     let mut ctx = WasiCtxBuilder::new();
17 
18     let (exitcode, stdout) = run_with_stdout("duplicate_import.wat", &mut ctx).unwrap();
19 
20     assert_eq!(stdout, "duplicate import works!\n");
21     assert_eq!(exitcode, 0);
22 }
23 
24 #[test]
hello()25 fn hello() {
26     let mut ctx = WasiCtxBuilder::new();
27     ctx.args(["hello"].iter());
28 
29     let (exitcode, stdout) = run_with_stdout(
30         Path::new(LUCET_WASI_ROOT).join("examples").join("hello.c"),
31         &mut ctx,
32     )
33     .unwrap();
34 
35     assert_eq!(exitcode, 0);
36     assert_eq!(&stdout, "hello, wasi!\n");
37 }
38 
39 #[test]
hello_args()40 fn hello_args() {
41     let mut ctx = WasiCtxBuilder::new();
42     ctx.args(["hello", "test suite"].iter());
43 
44     let (exitcode, stdout) = run_with_stdout(
45         Path::new(LUCET_WASI_ROOT).join("examples").join("hello.c"),
46         &mut ctx,
47     )
48     .unwrap();
49 
50     assert_eq!(exitcode, 0);
51     assert_eq!(&stdout, "hello, test suite!\n");
52 }
53 
54 #[test]
hello_env()55 fn hello_env() {
56     let mut ctx = WasiCtxBuilder::new();
57     ctx.args(["hello", "test suite"].iter());
58     ctx.env("GREETING", "goodbye");
59 
60     let (exitcode, stdout) = run_with_stdout(
61         Path::new(LUCET_WASI_ROOT).join("examples").join("hello.c"),
62         &mut ctx,
63     )
64     .unwrap();
65 
66     assert_eq!(exitcode, 0);
67     assert_eq!(&stdout, "goodbye, test suite!\n");
68 }
69 
70 #[test]
exitcode()71 fn exitcode() {
72     let ctx = WasiCtx::new(["exitcode"].iter()).unwrap();
73 
74     let exitcode = run("exitcode.c", ctx).unwrap();
75 
76     assert_eq!(exitcode, 120);
77 }
78 
79 #[test]
clock_getres()80 fn clock_getres() {
81     let ctx = WasiCtx::new(["clock_getres"].iter()).unwrap();
82 
83     let exitcode = run("clock_getres.c", ctx).unwrap();
84 
85     assert_eq!(exitcode, 0);
86 }
87 
88 #[test]
getrusage()89 fn getrusage() {
90     let ctx = WasiCtx::new(["getrusage"].iter()).unwrap();
91 
92     let exitcode = run("getrusage.c", ctx).unwrap();
93 
94     assert_eq!(exitcode, 0);
95 }
96 
97 #[test]
gettimeofday()98 fn gettimeofday() {
99     let ctx = WasiCtx::new(["gettimeofday"].iter()).unwrap();
100 
101     let exitcode = run("gettimeofday.c", ctx).unwrap();
102 
103     assert_eq!(exitcode, 0);
104 }
105 
106 #[test]
getentropy()107 fn getentropy() {
108     let ctx = WasiCtx::new(["getentropy"].iter()).unwrap();
109 
110     let exitcode = run("getentropy.c", ctx).unwrap();
111 
112     assert_eq!(exitcode, 0);
113 }
114 
115 #[test]
stdin()116 fn stdin() {
117     use std::io::Write;
118     use std::os::unix::io::FromRawFd;
119 
120     let (pipe_out, pipe_in) = nix::unistd::pipe().expect("can create pipe");
121 
122     let mut stdin_file = unsafe { File::from_raw_fd(pipe_in) };
123     write!(stdin_file, "hello from stdin!").expect("pipe write succeeds");
124     drop(stdin_file);
125 
126     let mut ctx = WasiCtxBuilder::new();
127     ctx.args(["stdin"].iter());
128     ctx.stdin(unsafe { File::from_raw_fd(pipe_out) });
129 
130     let (exitcode, stdout) = run_with_stdout("stdin.c", &mut ctx).unwrap();
131 
132     assert_eq!(exitcode, 0);
133     assert_eq!(&stdout, "hello from stdin!");
134 }
135 
136 #[test]
preopen_populates()137 fn preopen_populates() {
138     let tmpdir = TempDir::new().unwrap();
139     let preopen_host_path = tmpdir.path().join("preopen");
140     std::fs::create_dir(&preopen_host_path).unwrap();
141     let preopen_dir = File::open(preopen_host_path).unwrap();
142 
143     let mut ctx = WasiCtxBuilder::new();
144     ctx.args(["preopen_populates"].iter());
145     ctx.preopened_dir(preopen_dir, "/preopen");
146     let ctx = ctx.build().expect("can build WasiCtx");
147 
148     let exitcode = run("preopen_populates.c", ctx).unwrap();
149 
150     drop(tmpdir);
151 
152     assert_eq!(exitcode, 0);
153 }
154 
155 #[test]
write_file()156 fn write_file() {
157     let tmpdir = TempDir::new().unwrap();
158     let preopen_host_path = tmpdir.path().join("preopen");
159     std::fs::create_dir(&preopen_host_path).unwrap();
160     let preopen_dir = File::open(&preopen_host_path).unwrap();
161 
162     let mut ctx = WasiCtxBuilder::new();
163     ctx.args(["write_file"].iter());
164     ctx.preopened_dir(preopen_dir, "/sandbox");
165     let ctx = ctx.build().expect("can build WasiCtx");
166 
167     let exitcode = run("write_file.c", ctx).unwrap();
168     assert_eq!(exitcode, 0);
169 
170     let output = std::fs::read(preopen_host_path.join("output.txt")).unwrap();
171 
172     assert_eq!(output.as_slice(), b"hello, file!");
173 
174     drop(tmpdir);
175 }
176 
177 #[test]
read_file()178 fn read_file() {
179     const MESSAGE: &str = "hello from file!";
180     let tmpdir = TempDir::new().unwrap();
181     let preopen_host_path = tmpdir.path().join("preopen");
182     std::fs::create_dir(&preopen_host_path).unwrap();
183 
184     std::fs::write(preopen_host_path.join("input.txt"), MESSAGE).unwrap();
185 
186     let preopen_dir = File::open(&preopen_host_path).unwrap();
187 
188     let mut ctx = WasiCtxBuilder::new();
189     ctx.args(["read_file"].iter());
190     ctx.preopened_dir(preopen_dir, "/sandbox");
191 
192     let (exitcode, stdout) = run_with_stdout("read_file.c", &mut ctx).unwrap();
193     assert_eq!(exitcode, 0);
194 
195     assert_eq!(&stdout, MESSAGE);
196 
197     drop(tmpdir);
198 }
199 
200 #[test]
read_file_twice()201 fn read_file_twice() {
202     const MESSAGE: &str = "hello from file!";
203     let tmpdir = TempDir::new().unwrap();
204     let preopen_host_path = tmpdir.path().join("preopen");
205     std::fs::create_dir(&preopen_host_path).unwrap();
206 
207     std::fs::write(preopen_host_path.join("input.txt"), MESSAGE).unwrap();
208 
209     let preopen_dir = File::open(&preopen_host_path).unwrap();
210 
211     let mut ctx = WasiCtxBuilder::new();
212     ctx.args(["read_file_twice"].iter());
213     ctx.preopened_dir(preopen_dir, "/sandbox");
214 
215     let (exitcode, stdout) = run_with_stdout("read_file_twice.c", &mut ctx).unwrap();
216     assert_eq!(exitcode, 0);
217 
218     let double_message = format!("{}{}", MESSAGE, MESSAGE);
219     assert_eq!(stdout, double_message);
220 
221     drop(tmpdir);
222 }
223 
224 #[test]
cant_dotdot()225 fn cant_dotdot() {
226     const MESSAGE: &str = "hello from file!";
227     let tmpdir = TempDir::new().unwrap();
228     let preopen_host_path = tmpdir.path().join("preopen");
229     std::fs::create_dir(&preopen_host_path).unwrap();
230 
231     std::fs::write(
232         preopen_host_path.parent().unwrap().join("outside.txt"),
233         MESSAGE,
234     )
235     .unwrap();
236 
237     let preopen_dir = File::open(&preopen_host_path).unwrap();
238 
239     let mut ctx = WasiCtxBuilder::new();
240     ctx.args(["cant_dotdot"].iter());
241     ctx.preopened_dir(preopen_dir, "/sandbox");
242     let ctx = ctx.build().unwrap();
243 
244     let exitcode = run("cant_dotdot.c", ctx).unwrap();
245     assert_eq!(exitcode, 0);
246 
247     drop(tmpdir);
248 }
249 
250 #[ignore] // needs fd_readdir
251 #[test]
notdir()252 fn notdir() {
253     const MESSAGE: &str = "hello from file!";
254     let tmpdir = TempDir::new().unwrap();
255     let preopen_host_path = tmpdir.path().join("preopen");
256     std::fs::create_dir(&preopen_host_path).unwrap();
257 
258     std::fs::write(preopen_host_path.join("notadir"), MESSAGE).unwrap();
259 
260     let preopen_dir = File::open(&preopen_host_path).unwrap();
261 
262     let mut ctx = WasiCtxBuilder::new();
263     ctx.args(["notdir"].iter());
264     ctx.preopened_dir(preopen_dir, "/sandbox");
265     let ctx = ctx.build().unwrap();
266 
267     let exitcode = run("notdir.c", ctx).unwrap();
268     assert_eq!(exitcode, 0);
269 
270     drop(tmpdir);
271 }
272 
273 #[test]
follow_symlink()274 fn follow_symlink() {
275     const MESSAGE: &str = "hello from file!";
276 
277     let tmpdir = TempDir::new().unwrap();
278     let preopen_host_path = tmpdir.path().join("preopen");
279     let subdir1 = preopen_host_path.join("subdir1");
280     let subdir2 = preopen_host_path.join("subdir2");
281     std::fs::create_dir_all(&subdir1).unwrap();
282     std::fs::create_dir_all(&subdir2).unwrap();
283 
284     std::fs::write(subdir1.join("input.txt"), MESSAGE).unwrap();
285 
286     std::os::unix::fs::symlink("../subdir1/input.txt", subdir2.join("input_link.txt")).unwrap();
287 
288     let preopen_dir = File::open(&preopen_host_path).unwrap();
289 
290     let mut ctx = WasiCtxBuilder::new();
291     ctx.args(["follow_symlink"].iter());
292     ctx.preopened_dir(preopen_dir, "/sandbox");
293 
294     let (exitcode, stdout) = run_with_stdout("follow_symlink.c", &mut ctx).unwrap();
295     assert_eq!(exitcode, 0);
296     assert_eq!(&stdout, MESSAGE);
297 
298     drop(tmpdir);
299 }
300 
301 #[test]
symlink_loop()302 fn symlink_loop() {
303     let tmpdir = TempDir::new().unwrap();
304     let preopen_host_path = tmpdir.path().join("preopen");
305     let subdir1 = preopen_host_path.join("subdir1");
306     let subdir2 = preopen_host_path.join("subdir2");
307     std::fs::create_dir_all(&subdir1).unwrap();
308     std::fs::create_dir_all(&subdir2).unwrap();
309 
310     std::os::unix::fs::symlink("../subdir1/loop1", subdir2.join("loop2")).unwrap();
311     std::os::unix::fs::symlink("../subdir2/loop2", subdir1.join("loop1")).unwrap();
312 
313     let preopen_dir = File::open(&preopen_host_path).unwrap();
314 
315     let mut ctx = WasiCtxBuilder::new();
316     ctx.args(["symlink_loop"].iter());
317     ctx.preopened_dir(preopen_dir, "/sandbox");
318     let ctx = ctx.build().unwrap();
319 
320     let exitcode = run("symlink_loop.c", ctx).unwrap();
321     assert_eq!(exitcode, 0);
322 
323     drop(tmpdir);
324 }
325 
326 #[test]
symlink_escape()327 fn symlink_escape() {
328     const MESSAGE: &str = "hello from file!";
329 
330     let tmpdir = TempDir::new().unwrap();
331     let preopen_host_path = tmpdir.path().join("preopen");
332     let subdir = preopen_host_path.join("subdir");
333     std::fs::create_dir_all(&subdir).unwrap();
334 
335     std::fs::write(
336         preopen_host_path.parent().unwrap().join("outside.txt"),
337         MESSAGE,
338     )
339     .unwrap();
340     std::os::unix::fs::symlink("../../outside.txt", subdir.join("outside.txt")).unwrap();
341 
342     let preopen_dir = File::open(&preopen_host_path).unwrap();
343 
344     let mut ctx = WasiCtxBuilder::new();
345     ctx.args(["symlink_escape"].iter());
346     ctx.preopened_dir(preopen_dir, "/sandbox");
347     let ctx = ctx.build().unwrap();
348 
349     let exitcode = run("symlink_escape.c", ctx).unwrap();
350     assert_eq!(exitcode, 0);
351 
352     drop(tmpdir);
353 }
354 
355 #[test]
pseudoquine()356 fn pseudoquine() {
357     let examples_dir = Path::new(LUCET_WASI_ROOT).join("examples");
358     let pseudoquine_c = examples_dir.join("pseudoquine.c");
359 
360     let mut ctx = WasiCtxBuilder::new();
361     ctx.args(["pseudoquine"].iter());
362     ctx.preopened_dir(File::open(examples_dir).unwrap(), "/examples");
363 
364     let (exitcode, stdout) = run_with_stdout(&pseudoquine_c, &mut ctx).unwrap();
365 
366     assert_eq!(exitcode, 0);
367 
368     let expected = std::fs::read_to_string(&pseudoquine_c).unwrap();
369 
370     assert_eq!(stdout, expected);
371 }
372 
373 // ACF 2019-10-03: temporarily disabled until we figure out why it's behaving differently only in
374 // one CI environment
375 #[ignore]
376 #[test]
poll()377 fn poll() {
378     let mut ctx = WasiCtxBuilder::new();
379     ctx.args(["poll"].iter());
380     let exitcode = run_with_null_stdin("poll.c", &mut ctx).unwrap();
381     assert_eq!(exitcode, 0);
382 }
383 
384 #[test]
stat()385 fn stat() {
386     let tmpdir = TempDir::new().unwrap();
387     let preopen_host_path = tmpdir.path().join("preopen");
388     std::fs::create_dir(&preopen_host_path).unwrap();
389     let preopen_dir = File::open(&preopen_host_path).unwrap();
390     let mut ctx = WasiCtxBuilder::new();
391     ctx.args(["stat"].iter());
392     ctx.preopened_dir(preopen_dir, "/sandbox");
393     let ctx = ctx.build().expect("can build WasiCtx");
394     let exitcode = run("stat.c", ctx).unwrap();
395     assert_eq!(exitcode, 0);
396 }
397 
398 #[test]
fs()399 fn fs() {
400     let tmpdir = TempDir::new().unwrap();
401     let preopen_host_path = tmpdir.path().join("preopen");
402     std::fs::create_dir(&preopen_host_path).unwrap();
403     let preopen_dir = File::open(&preopen_host_path).unwrap();
404     let mut ctx = WasiCtxBuilder::new();
405     ctx.args(["stat"].iter());
406     ctx.preopened_dir(preopen_dir, "/sandbox");
407     let ctx = ctx.build().expect("can build WasiCtx");
408     let exitcode = run("fs.c", ctx).unwrap();
409     assert_eq!(exitcode, 0);
410 }
411