]> git.lizzy.rs Git - rust.git/blob - src/tools/miri/tests/pass-dep/shims/libc-misc.rs
Auto merge of #105323 - cjgillot:simplify-const-prop, r=davidtwco
[rust.git] / src / tools / miri / tests / pass-dep / shims / libc-misc.rs
1 //@ignore-target-windows: No libc on Windows
2 //@compile-flags: -Zmiri-disable-isolation
3 #![feature(io_error_more)]
4
5 use std::fs::{remove_file, File};
6 use std::os::unix::io::AsRawFd;
7 use std::path::PathBuf;
8
9 fn tmp() -> PathBuf {
10     use std::ffi::{CStr, CString};
11
12     let path = std::env::var("MIRI_TEMP")
13         .unwrap_or_else(|_| std::env::temp_dir().into_os_string().into_string().unwrap());
14     // These are host paths. We need to convert them to the target.
15     let path = CString::new(path).unwrap();
16     let mut out = Vec::with_capacity(1024);
17
18     unsafe {
19         extern "Rust" {
20             fn miri_host_to_target_path(path: *const i8, out: *mut i8, out_size: usize) -> usize;
21         }
22         let ret = miri_host_to_target_path(path.as_ptr(), out.as_mut_ptr(), out.capacity());
23         assert_eq!(ret, 0);
24         let out = CStr::from_ptr(out.as_ptr()).to_str().unwrap();
25         PathBuf::from(out)
26     }
27 }
28
29 /// Test allocating variant of `realpath`.
30 fn test_posix_realpath_alloc() {
31     use std::ffi::OsString;
32     use std::ffi::{CStr, CString};
33     use std::os::unix::ffi::OsStrExt;
34     use std::os::unix::ffi::OsStringExt;
35
36     let buf;
37     let path = tmp().join("miri_test_libc_posix_realpath_alloc");
38     let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed");
39
40     // Cleanup before test.
41     remove_file(&path).ok();
42     // Create file.
43     drop(File::create(&path).unwrap());
44     unsafe {
45         let r = libc::realpath(c_path.as_ptr(), std::ptr::null_mut());
46         assert!(!r.is_null());
47         buf = CStr::from_ptr(r).to_bytes().to_vec();
48         libc::free(r as *mut _);
49     }
50     let canonical = PathBuf::from(OsString::from_vec(buf));
51     assert_eq!(path.file_name(), canonical.file_name());
52
53     // Cleanup after test.
54     remove_file(&path).unwrap();
55 }
56
57 /// Test non-allocating variant of `realpath`.
58 fn test_posix_realpath_noalloc() {
59     use std::ffi::{CStr, CString};
60     use std::os::unix::ffi::OsStrExt;
61
62     let path = tmp().join("miri_test_libc_posix_realpath_noalloc");
63     let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed");
64
65     let mut v = vec![0; libc::PATH_MAX as usize];
66
67     // Cleanup before test.
68     remove_file(&path).ok();
69     // Create file.
70     drop(File::create(&path).unwrap());
71     unsafe {
72         let r = libc::realpath(c_path.as_ptr(), v.as_mut_ptr());
73         assert!(!r.is_null());
74     }
75     let c = unsafe { CStr::from_ptr(v.as_ptr()) };
76     let canonical = PathBuf::from(c.to_str().expect("CStr to str"));
77
78     assert_eq!(path.file_name(), canonical.file_name());
79
80     // Cleanup after test.
81     remove_file(&path).unwrap();
82 }
83
84 /// Test failure cases for `realpath`.
85 fn test_posix_realpath_errors() {
86     use std::ffi::CString;
87     use std::io::ErrorKind;
88
89     // Test non-existent path returns an error.
90     let c_path = CString::new("./nothing_to_see_here").expect("CString::new failed");
91     let r = unsafe { libc::realpath(c_path.as_ptr(), std::ptr::null_mut()) };
92     assert!(r.is_null());
93     let e = std::io::Error::last_os_error();
94     assert_eq!(e.raw_os_error(), Some(libc::ENOENT));
95     assert_eq!(e.kind(), ErrorKind::NotFound);
96 }
97
98 #[cfg(target_os = "linux")]
99 fn test_posix_fadvise() {
100     use std::convert::TryInto;
101     use std::io::Write;
102
103     let path = tmp().join("miri_test_libc_posix_fadvise.txt");
104     // Cleanup before test
105     remove_file(&path).ok();
106
107     // Set up an open file
108     let mut file = File::create(&path).unwrap();
109     let bytes = b"Hello, World!\n";
110     file.write(bytes).unwrap();
111
112     // Test calling posix_fadvise on a file.
113     let result = unsafe {
114         libc::posix_fadvise(
115             file.as_raw_fd(),
116             0,
117             bytes.len().try_into().unwrap(),
118             libc::POSIX_FADV_DONTNEED,
119         )
120     };
121     drop(file);
122     remove_file(&path).unwrap();
123     assert_eq!(result, 0);
124 }
125
126 #[cfg(target_os = "linux")]
127 fn test_sync_file_range() {
128     use std::io::Write;
129
130     let path = tmp().join("miri_test_libc_sync_file_range.txt");
131     // Cleanup before test.
132     remove_file(&path).ok();
133
134     // Write to a file.
135     let mut file = File::create(&path).unwrap();
136     let bytes = b"Hello, World!\n";
137     file.write(bytes).unwrap();
138
139     // Test calling sync_file_range on the file.
140     let result_1 = unsafe {
141         libc::sync_file_range(
142             file.as_raw_fd(),
143             0,
144             0,
145             libc::SYNC_FILE_RANGE_WAIT_BEFORE
146                 | libc::SYNC_FILE_RANGE_WRITE
147                 | libc::SYNC_FILE_RANGE_WAIT_AFTER,
148         )
149     };
150     drop(file);
151
152     // Test calling sync_file_range on a file opened for reading.
153     let file = File::open(&path).unwrap();
154     let result_2 = unsafe {
155         libc::sync_file_range(
156             file.as_raw_fd(),
157             0,
158             0,
159             libc::SYNC_FILE_RANGE_WAIT_BEFORE
160                 | libc::SYNC_FILE_RANGE_WRITE
161                 | libc::SYNC_FILE_RANGE_WAIT_AFTER,
162         )
163     };
164     drop(file);
165
166     remove_file(&path).unwrap();
167     assert_eq!(result_1, 0);
168     assert_eq!(result_2, 0);
169 }
170
171 /// Tests whether each thread has its own `__errno_location`.
172 fn test_thread_local_errno() {
173     #[cfg(target_os = "linux")]
174     use libc::__errno_location;
175     #[cfg(any(target_os = "macos", target_os = "freebsd"))]
176     use libc::__error as __errno_location;
177
178     unsafe {
179         *__errno_location() = 0xBEEF;
180         std::thread::spawn(|| {
181             assert_eq!(*__errno_location(), 0);
182             *__errno_location() = 0xBAD1DEA;
183             assert_eq!(*__errno_location(), 0xBAD1DEA);
184         })
185         .join()
186         .unwrap();
187         assert_eq!(*__errno_location(), 0xBEEF);
188     }
189 }
190
191 /// Tests whether clock support exists at all
192 fn test_clocks() {
193     let mut tp = std::mem::MaybeUninit::<libc::timespec>::uninit();
194     let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, tp.as_mut_ptr()) };
195     assert_eq!(is_error, 0);
196     let is_error = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, tp.as_mut_ptr()) };
197     assert_eq!(is_error, 0);
198     #[cfg(target_os = "linux")]
199     {
200         let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME_COARSE, tp.as_mut_ptr()) };
201         assert_eq!(is_error, 0);
202         let is_error =
203             unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_COARSE, tp.as_mut_ptr()) };
204         assert_eq!(is_error, 0);
205     }
206     #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
207     {
208         let is_error = unsafe { libc::clock_gettime(libc::CLOCK_UPTIME_RAW, tp.as_mut_ptr()) };
209         assert_eq!(is_error, 0);
210     }
211 }
212
213 fn test_posix_gettimeofday() {
214     let mut tp = std::mem::MaybeUninit::<libc::timeval>::uninit();
215     let tz = std::ptr::null_mut::<libc::timezone>();
216     #[cfg(target_os = "macos")] // `tz` has a different type on macOS
217     let tz = tz as *mut libc::c_void;
218     let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz) };
219     assert_eq!(is_error, 0);
220     let tv = unsafe { tp.assume_init() };
221     assert!(tv.tv_sec > 0);
222     assert!(tv.tv_usec >= 0); // Theoretically this could be 0.
223
224     // Test that non-null tz returns an error.
225     let mut tz = std::mem::MaybeUninit::<libc::timezone>::uninit();
226     let tz_ptr = tz.as_mut_ptr();
227     #[cfg(target_os = "macos")] // `tz` has a different type on macOS
228     let tz_ptr = tz_ptr as *mut libc::c_void;
229     let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr) };
230     assert_eq!(is_error, -1);
231 }
232
233 fn test_isatty() {
234     // Testing whether our isatty shim returns the right value would require controlling whether
235     // these streams are actually TTYs, which is hard.
236     // For now, we just check that these calls are supported at all.
237     unsafe {
238         libc::isatty(libc::STDIN_FILENO);
239         libc::isatty(libc::STDOUT_FILENO);
240         libc::isatty(libc::STDERR_FILENO);
241
242         // But when we open a file, it is definitely not a TTY.
243         let path = tmp().join("notatty.txt");
244         // Cleanup before test.
245         remove_file(&path).ok();
246         let file = File::create(&path).unwrap();
247
248         assert_eq!(libc::isatty(file.as_raw_fd()), 0);
249         assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::ENOTTY);
250
251         // Cleanup after test.
252         drop(file);
253         remove_file(&path).unwrap();
254     }
255 }
256
257 fn test_posix_mkstemp() {
258     use std::ffi::CString;
259     use std::ffi::OsStr;
260     use std::os::unix::ffi::OsStrExt;
261     use std::os::unix::io::FromRawFd;
262     use std::path::Path;
263
264     let valid_template = "fooXXXXXX";
265     // C needs to own this as `mkstemp(3)` says:
266     // "Since it will be modified, `template` must not be a string constant, but
267     // should be declared as a character array."
268     // There seems to be no `as_mut_ptr` on `CString` so we need to use `into_raw`.
269     let ptr = CString::new(valid_template).unwrap().into_raw();
270     let fd = unsafe { libc::mkstemp(ptr) };
271     // Take ownership back in Rust to not leak memory.
272     let slice = unsafe { CString::from_raw(ptr) };
273     assert!(fd > 0);
274     let osstr = OsStr::from_bytes(slice.to_bytes());
275     let path: &Path = osstr.as_ref();
276     let name = path.file_name().unwrap().to_string_lossy();
277     assert!(name.ne("fooXXXXXX"));
278     assert!(name.starts_with("foo"));
279     assert_eq!(name.len(), 9);
280     assert_eq!(
281         name.chars().skip(3).filter(char::is_ascii_alphanumeric).collect::<Vec<char>>().len(),
282         6
283     );
284     let file = unsafe { File::from_raw_fd(fd) };
285     assert!(file.set_len(0).is_ok());
286
287     let invalid_templates = vec!["foo", "barXX", "XXXXXXbaz", "whatXXXXXXever", "X"];
288     for t in invalid_templates {
289         let ptr = CString::new(t).unwrap().into_raw();
290         let fd = unsafe { libc::mkstemp(ptr) };
291         let _ = unsafe { CString::from_raw(ptr) };
292         // "On error, -1 is returned, and errno is set to
293         // indicate the error"
294         assert_eq!(fd, -1);
295         let e = std::io::Error::last_os_error();
296         assert_eq!(e.raw_os_error(), Some(libc::EINVAL));
297         assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput);
298     }
299 }
300
301 fn main() {
302     test_posix_gettimeofday();
303     test_posix_mkstemp();
304
305     test_posix_realpath_alloc();
306     test_posix_realpath_noalloc();
307     test_posix_realpath_errors();
308
309     test_thread_local_errno();
310
311     test_isatty();
312     test_clocks();
313
314     #[cfg(target_os = "linux")]
315     {
316         test_posix_fadvise();
317         test_sync_file_range();
318     }
319 }