Skip to content

Commit d6074e0

Browse files
committed
split libc tests from stdlib tests
1 parent 2a8da35 commit d6074e0

9 files changed

+176
-120
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//@ignore-target-windows: no libc on Windows
2+
//@compile-flags: -Zmiri-isolation-error=warn-nobacktrace
3+
//@normalize-stderr-test: "(stat(x)?)" -> "$$STAT"
4+
5+
use std::ffi::CString;
6+
use std::fs;
7+
use std::io::{Error, ErrorKind};
8+
9+
fn main() {
10+
// test `fcntl`
11+
unsafe {
12+
assert_eq!(libc::fcntl(1, libc::F_DUPFD, 0), -1);
13+
assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EPERM));
14+
}
15+
16+
// test `readlink`
17+
let symlink_c_str = CString::new("foo.txt").unwrap();
18+
let mut buf = vec![0; "foo_link.txt".len() + 1];
19+
unsafe {
20+
assert_eq!(libc::readlink(symlink_c_str.as_ptr(), buf.as_mut_ptr(), buf.len()), -1);
21+
assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES));
22+
}
23+
24+
// test `stat`
25+
assert_eq!(fs::metadata("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
26+
// check that it is the right kind of `PermissionDenied`
27+
assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES));
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
warning: `fcntl` was made to return an error due to isolation
2+
3+
warning: `readlink` was made to return an error due to isolation
4+
5+
warning: `$STAT` was made to return an error due to isolation
6+

tests/pass-dep/shims/libc-fs.rs

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
//@ignore-target-windows: no libc on Windows
2+
//@compile-flags: -Zmiri-disable-isolation
3+
4+
#![feature(io_error_more)]
5+
#![feature(io_error_uncategorized)]
6+
7+
use std::convert::TryInto;
8+
use std::ffi::CString;
9+
use std::fs::{canonicalize, remove_file, File};
10+
use std::io::{Error, ErrorKind, Write};
11+
use std::os::unix::ffi::OsStrExt;
12+
use std::path::PathBuf;
13+
14+
fn main() {
15+
test_dup_stdout_stderr();
16+
test_canonicalize_too_long();
17+
test_readlink();
18+
test_file_open_unix_allow_two_args();
19+
test_file_open_unix_needs_three_args();
20+
test_file_open_unix_extra_third_arg();
21+
}
22+
23+
fn tmp() -> PathBuf {
24+
std::env::var("MIRI_TEMP")
25+
.map(|tmp| {
26+
// MIRI_TEMP is set outside of our emulated
27+
// program, so it may have path separators that don't
28+
// correspond to our target platform. We normalize them here
29+
// before constructing a `PathBuf`
30+
31+
#[cfg(windows)]
32+
return PathBuf::from(tmp.replace("/", "\\"));
33+
34+
#[cfg(not(windows))]
35+
return PathBuf::from(tmp.replace("\\", "/"));
36+
})
37+
.unwrap_or_else(|_| std::env::temp_dir())
38+
}
39+
40+
/// Prepare: compute filename and make sure the file does not exist.
41+
fn prepare(filename: &str) -> PathBuf {
42+
let path = tmp().join(filename);
43+
// Clean the paths for robustness.
44+
remove_file(&path).ok();
45+
path
46+
}
47+
48+
/// Prepare like above, and also write some initial content to the file.
49+
fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
50+
let path = prepare(filename);
51+
let mut file = File::create(&path).unwrap();
52+
file.write(content).unwrap();
53+
path
54+
}
55+
56+
fn test_file_open_unix_allow_two_args() {
57+
let path = prepare_with_content("test_file_open_unix_allow_two_args.txt", &[]);
58+
59+
let mut name = path.into_os_string();
60+
name.push("\0");
61+
let name_ptr = name.as_bytes().as_ptr().cast::<libc::c_char>();
62+
let _fd = unsafe { libc::open(name_ptr, libc::O_RDONLY) };
63+
}
64+
65+
fn test_file_open_unix_needs_three_args() {
66+
let path = prepare_with_content("test_file_open_unix_needs_three_args.txt", &[]);
67+
68+
let mut name = path.into_os_string();
69+
name.push("\0");
70+
let name_ptr = name.as_bytes().as_ptr().cast::<libc::c_char>();
71+
let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT, 0o666) };
72+
}
73+
74+
fn test_file_open_unix_extra_third_arg() {
75+
let path = prepare_with_content("test_file_open_unix_extra_third_arg.txt", &[]);
76+
77+
let mut name = path.into_os_string();
78+
name.push("\0");
79+
let name_ptr = name.as_bytes().as_ptr().cast::<libc::c_char>();
80+
let _fd = unsafe { libc::open(name_ptr, libc::O_RDONLY, 42) };
81+
}
82+
83+
fn test_dup_stdout_stderr() {
84+
let bytes = b"hello dup fd\n";
85+
unsafe {
86+
let new_stdout = libc::fcntl(1, libc::F_DUPFD, 0);
87+
let new_stderr = libc::fcntl(2, libc::F_DUPFD, 0);
88+
libc::write(new_stdout, bytes.as_ptr() as *const libc::c_void, bytes.len());
89+
libc::write(new_stderr, bytes.as_ptr() as *const libc::c_void, bytes.len());
90+
}
91+
}
92+
93+
fn test_canonicalize_too_long() {
94+
// Make sure we get an error for long paths.
95+
let too_long = "x/".repeat(libc::PATH_MAX.try_into().unwrap());
96+
assert!(canonicalize(too_long).is_err());
97+
}
98+
99+
fn test_readlink() {
100+
let bytes = b"Hello, World!\n";
101+
let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);
102+
let expected_path = path.as_os_str().as_bytes();
103+
104+
let symlink_path = prepare("miri_test_fs_symlink.txt");
105+
std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
106+
107+
// Test that the expected string gets written to a buffer of proper
108+
// length, and that a trailing null byte is not written.
109+
let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap();
110+
let symlink_c_ptr = symlink_c_str.as_ptr();
111+
112+
// Make the buf one byte larger than it needs to be,
113+
// and check that the last byte is not overwritten.
114+
let mut large_buf = vec![0xFF; expected_path.len() + 1];
115+
let res =
116+
unsafe { libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) };
117+
// Check that the resovled path was properly written into the buf.
118+
assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path);
119+
assert_eq!(large_buf.last(), Some(&0xFF));
120+
assert_eq!(res, large_buf.len() as isize - 1);
121+
122+
// Test that the resolved path is truncated if the provided buffer
123+
// is too small.
124+
let mut small_buf = [0u8; 2];
125+
let res =
126+
unsafe { libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) };
127+
assert_eq!(small_buf, &expected_path[..small_buf.len()]);
128+
assert_eq!(res, small_buf.len() as isize);
129+
130+
// Test that we report a proper error for a missing path.
131+
let bad_path = CString::new("MIRI_MISSING_FILE_NAME").unwrap();
132+
let res = unsafe {
133+
libc::readlink(bad_path.as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len())
134+
};
135+
assert_eq!(res, -1);
136+
assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound);
137+
}
File renamed without changes.
File renamed without changes.

tests/pass-dep/shims/libc-rsfs.stdout

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
hello dup fd

tests/pass-dep/shims/fs_with_isolation.rs renamed to tests/pass/shims/fs-with-isolation.rs

+1-18
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,14 @@
22
//@compile-flags: -Zmiri-isolation-error=warn-nobacktrace
33
//@normalize-stderr-test: "(stat(x)?)" -> "$$STAT"
44

5-
use std::ffi::CString;
65
use std::fs::{self, File};
7-
use std::io::{Error, ErrorKind};
6+
use std::io::ErrorKind;
87
use std::os::unix;
98

109
fn main() {
1110
// test `open`
1211
assert_eq!(File::create("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
1312

14-
// test `fcntl`
15-
unsafe {
16-
assert_eq!(libc::fcntl(1, libc::F_DUPFD, 0), -1);
17-
assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EPERM));
18-
}
19-
2013
// test `unlink`
2114
assert_eq!(fs::remove_file("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
2215

@@ -26,17 +19,8 @@ fn main() {
2619
ErrorKind::PermissionDenied
2720
);
2821

29-
// test `readlink`
30-
let symlink_c_str = CString::new("foo.txt").unwrap();
31-
let mut buf = vec![0; "foo_link.txt".len() + 1];
32-
unsafe {
33-
assert_eq!(libc::readlink(symlink_c_str.as_ptr(), buf.as_mut_ptr(), buf.len()), -1);
34-
assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES));
35-
}
36-
3722
// test `stat`
3823
assert_eq!(fs::metadata("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
39-
assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES));
4024

4125
// test `rename`
4226
assert_eq!(fs::rename("a.txt", "b.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
@@ -49,5 +33,4 @@ fn main() {
4933

5034
// test `opendir`
5135
assert_eq!(fs::read_dir("foo/bar").unwrap_err().kind(), ErrorKind::PermissionDenied);
52-
assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES));
5336
}

tests/pass-dep/shims/fs_with_isolation.stderr renamed to tests/pass/shims/fs-with-isolation.stderr

-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
warning: `open` was made to return an error due to isolation
22

3-
warning: `fcntl` was made to return an error due to isolation
4-
53
warning: `unlink` was made to return an error due to isolation
64

75
warning: `symlink` was made to return an error due to isolation
86

9-
warning: `readlink` was made to return an error due to isolation
10-
117
warning: `$STAT` was made to return an error due to isolation
128

139
warning: `rename` was made to return an error due to isolation

tests/pass-dep/shims/fs.rs renamed to tests/pass/shims/fs.rs

+3-98
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
#![feature(io_error_uncategorized)]
66

77
use std::collections::HashMap;
8-
use std::ffi::{CString, OsString};
8+
use std::ffi::OsString;
99
use std::fs::{
10-
create_dir, read_dir, read_link, remove_dir, remove_dir_all, remove_file, rename, File,
11-
OpenOptions,
10+
canonicalize, create_dir, read_dir, read_link, remove_dir, remove_dir_all, remove_file, rename,
11+
File, OpenOptions,
1212
};
1313
use std::io::{Error, ErrorKind, Read, Result, Seek, SeekFrom, Write};
1414
use std::path::{Path, PathBuf};
@@ -26,13 +26,7 @@ fn main() {
2626
test_rename();
2727
test_directory();
2828
test_canonicalize();
29-
test_dup_stdout_stderr();
3029
test_from_raw_os_error();
31-
32-
// These all require unix, if the test is changed to no longer `ignore-windows`, move these to a unix test
33-
test_file_open_unix_allow_two_args();
34-
test_file_open_unix_needs_three_args();
35-
test_file_open_unix_extra_third_arg();
3630
}
3731

3832
fn tmp() -> PathBuf {
@@ -101,39 +95,6 @@ fn test_file() {
10195
remove_file(&path).unwrap();
10296
}
10397

104-
fn test_file_open_unix_allow_two_args() {
105-
use std::os::unix::ffi::OsStrExt;
106-
107-
let path = prepare_with_content("test_file_open_unix_allow_two_args.txt", &[]);
108-
109-
let mut name = path.into_os_string();
110-
name.push("\0");
111-
let name_ptr = name.as_bytes().as_ptr().cast::<libc::c_char>();
112-
let _fd = unsafe { libc::open(name_ptr, libc::O_RDONLY) };
113-
}
114-
115-
fn test_file_open_unix_needs_three_args() {
116-
use std::os::unix::ffi::OsStrExt;
117-
118-
let path = prepare_with_content("test_file_open_unix_needs_three_args.txt", &[]);
119-
120-
let mut name = path.into_os_string();
121-
name.push("\0");
122-
let name_ptr = name.as_bytes().as_ptr().cast::<libc::c_char>();
123-
let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT, 0o666) };
124-
}
125-
126-
fn test_file_open_unix_extra_third_arg() {
127-
use std::os::unix::ffi::OsStrExt;
128-
129-
let path = prepare_with_content("test_file_open_unix_extra_third_arg.txt", &[]);
130-
131-
let mut name = path.into_os_string();
132-
name.push("\0");
133-
let name_ptr = name.as_bytes().as_ptr().cast::<libc::c_char>();
134-
let _fd = unsafe { libc::open(name_ptr, libc::O_RDONLY, 42) };
135-
}
136-
13798
fn test_file_clone() {
13899
let bytes = b"Hello, World!\n";
139100
let path = prepare_with_content("miri_test_fs_file_clone.txt", bytes);
@@ -279,46 +240,6 @@ fn test_symlink() {
279240
symlink_file.read_to_end(&mut contents).unwrap();
280241
assert_eq!(bytes, contents.as_slice());
281242

282-
#[cfg(unix)]
283-
{
284-
use std::os::unix::ffi::OsStrExt;
285-
286-
let expected_path = path.as_os_str().as_bytes();
287-
288-
// Test that the expected string gets written to a buffer of proper
289-
// length, and that a trailing null byte is not written.
290-
let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap();
291-
let symlink_c_ptr = symlink_c_str.as_ptr();
292-
293-
// Make the buf one byte larger than it needs to be,
294-
// and check that the last byte is not overwritten.
295-
let mut large_buf = vec![0xFF; expected_path.len() + 1];
296-
let res = unsafe {
297-
libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len())
298-
};
299-
// Check that the resovled path was properly written into the buf.
300-
assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path);
301-
assert_eq!(large_buf.last(), Some(&0xFF));
302-
assert_eq!(res, large_buf.len() as isize - 1);
303-
304-
// Test that the resolved path is truncated if the provided buffer
305-
// is too small.
306-
let mut small_buf = [0u8; 2];
307-
let res = unsafe {
308-
libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len())
309-
};
310-
assert_eq!(small_buf, &expected_path[..small_buf.len()]);
311-
assert_eq!(res, small_buf.len() as isize);
312-
313-
// Test that we report a proper error for a missing path.
314-
let bad_path = CString::new("MIRI_MISSING_FILE_NAME").unwrap();
315-
let res = unsafe {
316-
libc::readlink(bad_path.as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len())
317-
};
318-
assert_eq!(res, -1);
319-
assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound);
320-
}
321-
322243
// Test that metadata of a symbolic link (i.e., the file it points to) is correct.
323244
check_metadata(bytes, &symlink_path).unwrap();
324245
// Test that the metadata of a symbolic link is correct when not following it.
@@ -369,7 +290,6 @@ fn test_rename() {
369290
}
370291

371292
fn test_canonicalize() {
372-
use std::fs::canonicalize;
373293
let dir_path = prepare_dir("miri_test_fs_dir");
374294
create_dir(&dir_path).unwrap();
375295
let path = dir_path.join("test_file");
@@ -379,11 +299,6 @@ fn test_canonicalize() {
379299
assert_eq!(p.to_string_lossy().find('.'), None);
380300

381301
remove_dir_all(&dir_path).unwrap();
382-
383-
// Make sure we get an error for long paths.
384-
use std::convert::TryInto;
385-
let too_long = "x/".repeat(libc::PATH_MAX.try_into().unwrap());
386-
assert!(canonicalize(too_long).is_err());
387302
}
388303

389304
fn test_directory() {
@@ -440,16 +355,6 @@ fn test_directory() {
440355
remove_dir_all(&dir_path).unwrap();
441356
}
442357

443-
fn test_dup_stdout_stderr() {
444-
let bytes = b"hello dup fd\n";
445-
unsafe {
446-
let new_stdout = libc::fcntl(1, libc::F_DUPFD, 0);
447-
let new_stderr = libc::fcntl(2, libc::F_DUPFD, 0);
448-
libc::write(new_stdout, bytes.as_ptr() as *const libc::c_void, bytes.len());
449-
libc::write(new_stderr, bytes.as_ptr() as *const libc::c_void, bytes.len());
450-
}
451-
}
452-
453358
fn test_from_raw_os_error() {
454359
let code = 6; // not a code that std or Miri know
455360
let error = Error::from_raw_os_error(code);

0 commit comments

Comments
 (0)