1 //@ignore-target-windows: No libc on Windows
2 //@compile-flags: -Zmiri-disable-isolation
3 #![feature(io_error_more)]
4 #![feature(rustc_private)]
6 use std::fs::{remove_file, File};
7 use std::os::unix::io::AsRawFd;
8 use std::path::PathBuf;
11 std::env::var("MIRI_TEMP")
13 // MIRI_TEMP is set outside of our emulated
14 // program, so it may have path separators that don't
15 // correspond to our target platform. We normalize them here
16 // before constructing a `PathBuf`
17 return PathBuf::from(tmp.replace("\\", "/"));
19 .unwrap_or_else(|_| std::env::temp_dir())
22 /// Test allocating variant of `realpath`.
23 fn test_posix_realpath_alloc() {
24 use std::ffi::OsString;
25 use std::ffi::{CStr, CString};
26 use std::os::unix::ffi::OsStrExt;
27 use std::os::unix::ffi::OsStringExt;
30 let path = tmp().join("miri_test_libc_posix_realpath_alloc");
31 let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed");
33 // Cleanup before test.
34 remove_file(&path).ok();
36 drop(File::create(&path).unwrap());
38 let r = libc::realpath(c_path.as_ptr(), std::ptr::null_mut());
39 assert!(!r.is_null());
40 buf = CStr::from_ptr(r).to_bytes().to_vec();
41 libc::free(r as *mut _);
43 let canonical = PathBuf::from(OsString::from_vec(buf));
44 assert_eq!(path.file_name(), canonical.file_name());
46 // Cleanup after test.
47 remove_file(&path).unwrap();
50 /// Test non-allocating variant of `realpath`.
51 fn test_posix_realpath_noalloc() {
52 use std::ffi::{CStr, CString};
53 use std::os::unix::ffi::OsStrExt;
55 let path = tmp().join("miri_test_libc_posix_realpath_noalloc");
56 let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed");
58 let mut v = vec![0; libc::PATH_MAX as usize];
60 // Cleanup before test.
61 remove_file(&path).ok();
63 drop(File::create(&path).unwrap());
65 let r = libc::realpath(c_path.as_ptr(), v.as_mut_ptr());
66 assert!(!r.is_null());
68 let c = unsafe { CStr::from_ptr(v.as_ptr()) };
69 let canonical = PathBuf::from(c.to_str().expect("CStr to str"));
71 assert_eq!(path.file_name(), canonical.file_name());
73 // Cleanup after test.
74 remove_file(&path).unwrap();
77 /// Test failure cases for `realpath`.
78 fn test_posix_realpath_errors() {
79 use std::ffi::CString;
80 use std::io::ErrorKind;
82 // Test non-existent path returns an error.
83 let c_path = CString::new("./nothing_to_see_here").expect("CString::new failed");
84 let r = unsafe { libc::realpath(c_path.as_ptr(), std::ptr::null_mut()) };
86 let e = std::io::Error::last_os_error();
87 assert_eq!(e.raw_os_error(), Some(libc::ENOENT));
88 assert_eq!(e.kind(), ErrorKind::NotFound);
91 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
92 fn test_posix_fadvise() {
93 use std::convert::TryInto;
96 let path = tmp().join("miri_test_libc_posix_fadvise.txt");
97 // Cleanup before test
98 remove_file(&path).ok();
100 // Set up an open file
101 let mut file = File::create(&path).unwrap();
102 let bytes = b"Hello, World!\n";
103 file.write(bytes).unwrap();
105 // Test calling posix_fadvise on a file.
106 let result = unsafe {
110 bytes.len().try_into().unwrap(),
111 libc::POSIX_FADV_DONTNEED,
115 remove_file(&path).unwrap();
116 assert_eq!(result, 0);
119 #[cfg(any(target_os = "linux"))]
120 fn test_sync_file_range() {
123 let path = tmp().join("miri_test_libc_sync_file_range.txt");
124 // Cleanup before test.
125 remove_file(&path).ok();
128 let mut file = File::create(&path).unwrap();
129 let bytes = b"Hello, World!\n";
130 file.write(bytes).unwrap();
132 // Test calling sync_file_range on the file.
133 let result_1 = unsafe {
134 libc::sync_file_range(
138 libc::SYNC_FILE_RANGE_WAIT_BEFORE
139 | libc::SYNC_FILE_RANGE_WRITE
140 | libc::SYNC_FILE_RANGE_WAIT_AFTER,
145 // Test calling sync_file_range on a file opened for reading.
146 let file = File::open(&path).unwrap();
147 let result_2 = unsafe {
148 libc::sync_file_range(
152 libc::SYNC_FILE_RANGE_WAIT_BEFORE
153 | libc::SYNC_FILE_RANGE_WRITE
154 | libc::SYNC_FILE_RANGE_WAIT_AFTER,
159 remove_file(&path).unwrap();
160 assert_eq!(result_1, 0);
161 assert_eq!(result_2, 0);
164 fn test_mutex_libc_init_recursive() {
166 let mut attr: libc::pthread_mutexattr_t = std::mem::zeroed();
167 assert_eq!(libc::pthread_mutexattr_init(&mut attr as *mut _), 0);
169 libc::pthread_mutexattr_settype(&mut attr as *mut _, libc::PTHREAD_MUTEX_RECURSIVE),
172 let mut mutex: libc::pthread_mutex_t = std::mem::zeroed();
173 assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mut attr as *mut _), 0);
174 assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
175 assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0);
176 assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
177 assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
178 assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0);
179 assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
180 assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
181 assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
182 assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), libc::EPERM);
183 assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0);
184 assert_eq!(libc::pthread_mutexattr_destroy(&mut attr as *mut _), 0);
188 fn test_mutex_libc_init_normal() {
190 let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed();
192 libc::pthread_mutexattr_settype(&mut mutexattr as *mut _, 0x12345678),
196 libc::pthread_mutexattr_settype(&mut mutexattr as *mut _, libc::PTHREAD_MUTEX_NORMAL),
199 let mut mutex: libc::pthread_mutex_t = std::mem::zeroed();
200 assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0);
201 assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
202 assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), libc::EBUSY);
203 assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
204 assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0);
205 assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
206 assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0);
210 fn test_mutex_libc_init_errorcheck() {
212 let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed();
214 libc::pthread_mutexattr_settype(
215 &mut mutexattr as *mut _,
216 libc::PTHREAD_MUTEX_ERRORCHECK,
220 let mut mutex: libc::pthread_mutex_t = std::mem::zeroed();
221 assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0);
222 assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
223 assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), libc::EBUSY);
224 assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), libc::EDEADLK);
225 assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
226 assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0);
227 assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
228 assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), libc::EPERM);
229 assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0);
233 // Only linux provides PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
234 // libc for macOS just has the default PTHREAD_MUTEX_INITIALIZER.
235 #[cfg(target_os = "linux")]
236 fn test_mutex_libc_static_initializer_recursive() {
237 let mutex = std::cell::UnsafeCell::new(libc::PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP);
239 assert_eq!(libc::pthread_mutex_lock(mutex.get()), 0);
240 assert_eq!(libc::pthread_mutex_trylock(mutex.get()), 0);
241 assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0);
242 assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0);
243 assert_eq!(libc::pthread_mutex_trylock(mutex.get()), 0);
244 assert_eq!(libc::pthread_mutex_lock(mutex.get()), 0);
245 assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0);
246 assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0);
247 assert_eq!(libc::pthread_mutex_unlock(mutex.get()), libc::EPERM);
248 assert_eq!(libc::pthread_mutex_destroy(mutex.get()), 0);
252 // Testing the behavior of std::sync::RwLock does not fully exercise the pthread rwlock shims, we
253 // need to go a layer deeper and test the behavior of the libc functions, because
254 // std::sys::unix::rwlock::RWLock itself keeps track of write_locked and num_readers.
255 fn test_rwlock_libc_static_initializer() {
256 let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
258 assert_eq!(libc::pthread_rwlock_rdlock(rw.get()), 0);
259 assert_eq!(libc::pthread_rwlock_rdlock(rw.get()), 0);
260 assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);
261 assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), 0);
262 assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);
263 assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY);
264 assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);
266 assert_eq!(libc::pthread_rwlock_wrlock(rw.get()), 0);
267 assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), libc::EBUSY);
268 assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY);
269 assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);
271 assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), 0);
272 assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), libc::EBUSY);
273 assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY);
274 assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0);
276 assert_eq!(libc::pthread_rwlock_destroy(rw.get()), 0);
280 /// Test whether the `prctl` shim correctly sets the thread name.
282 /// Note: `prctl` exists only on Linux.
283 #[cfg(any(target_os = "linux"))]
284 fn test_prctl_thread_name() {
286 use std::ffi::CString;
288 let mut buf = [255; 10];
290 libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr(), 0 as c_long, 0 as c_long, 0 as c_long),
293 // Rust runtime might set thread name, so we allow two options here.
294 assert!(&buf[..10] == b"<unnamed>\0" || &buf[..5] == b"main\0");
295 let thread_name = CString::new("hello").expect("CString::new failed");
299 thread_name.as_ptr(),
306 let mut buf = [255; 6];
308 libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr(), 0 as c_long, 0 as c_long, 0 as c_long),
311 assert_eq!(b"hello\0", &buf);
312 let long_thread_name = CString::new("01234567890123456789").expect("CString::new failed");
316 long_thread_name.as_ptr(),
323 let mut buf = [255; 16];
325 libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr(), 0 as c_long, 0 as c_long, 0 as c_long),
328 assert_eq!(b"012345678901234\0", &buf);
332 /// Tests whether each thread has its own `__errno_location`.
333 fn test_thread_local_errno() {
334 #[cfg(target_os = "linux")]
335 use libc::__errno_location;
336 #[cfg(any(target_os = "macos", target_os = "freebsd"))]
337 use libc::__error as __errno_location;
340 *__errno_location() = 0xBEEF;
341 std::thread::spawn(|| {
342 assert_eq!(*__errno_location(), 0);
343 *__errno_location() = 0xBAD1DEA;
344 assert_eq!(*__errno_location(), 0xBAD1DEA);
348 assert_eq!(*__errno_location(), 0xBEEF);
352 /// Tests whether clock support exists at all
353 #[cfg(any(target_os = "linux"))]
355 let mut tp = std::mem::MaybeUninit::<libc::timespec>::uninit();
356 let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, tp.as_mut_ptr()) };
357 assert_eq!(is_error, 0);
358 let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME_COARSE, tp.as_mut_ptr()) };
359 assert_eq!(is_error, 0);
360 let is_error = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, tp.as_mut_ptr()) };
361 assert_eq!(is_error, 0);
362 let is_error = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_COARSE, tp.as_mut_ptr()) };
363 assert_eq!(is_error, 0);
366 fn test_posix_gettimeofday() {
367 let mut tp = std::mem::MaybeUninit::<libc::timeval>::uninit();
368 let tz = std::ptr::null_mut::<libc::timezone>();
369 #[cfg(target_os = "macos")] // `tz` has a different type on macOS
370 let tz = tz as *mut libc::c_void;
371 let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz) };
372 assert_eq!(is_error, 0);
373 let tv = unsafe { tp.assume_init() };
374 assert!(tv.tv_sec > 0);
375 assert!(tv.tv_usec >= 0); // Theoretically this could be 0.
377 // Test that non-null tz returns an error.
378 let mut tz = std::mem::MaybeUninit::<libc::timezone>::uninit();
379 let tz_ptr = tz.as_mut_ptr();
380 #[cfg(target_os = "macos")] // `tz` has a different type on macOS
381 let tz_ptr = tz_ptr as *mut libc::c_void;
382 let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr) };
383 assert_eq!(is_error, -1);
387 // Testing whether our isatty shim returns the right value would require controlling whether
388 // these streams are actually TTYs, which is hard.
389 // For now, we just check that these calls are supported at all.
391 libc::isatty(libc::STDIN_FILENO);
392 libc::isatty(libc::STDOUT_FILENO);
393 libc::isatty(libc::STDERR_FILENO);
395 // But when we open a file, it is definitely not a TTY.
396 let path = tmp().join("notatty.txt");
397 // Cleanup before test.
398 remove_file(&path).ok();
399 let file = File::create(&path).unwrap();
401 assert_eq!(libc::isatty(file.as_raw_fd()), 0);
402 assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::ENOTTY);
404 // Cleanup after test.
406 remove_file(&path).unwrap();
410 fn test_posix_mkstemp() {
411 use std::ffi::CString;
413 use std::os::unix::ffi::OsStrExt;
414 use std::os::unix::io::FromRawFd;
417 let valid_template = "fooXXXXXX";
418 // C needs to own this as `mkstemp(3)` says:
419 // "Since it will be modified, `template` must not be a string constant, but
420 // should be declared as a character array."
421 // There seems to be no `as_mut_ptr` on `CString` so we need to use `into_raw`.
422 let ptr = CString::new(valid_template).unwrap().into_raw();
423 let fd = unsafe { libc::mkstemp(ptr) };
424 // Take ownership back in Rust to not leak memory.
425 let slice = unsafe { CString::from_raw(ptr) };
427 let osstr = OsStr::from_bytes(slice.to_bytes());
428 let path: &Path = osstr.as_ref();
429 let name = path.file_name().unwrap().to_string_lossy();
430 assert!(name.ne("fooXXXXXX"));
431 assert!(name.starts_with("foo"));
432 assert_eq!(name.len(), 9);
434 name.chars().skip(3).filter(char::is_ascii_alphanumeric).collect::<Vec<char>>().len(),
437 let file = unsafe { File::from_raw_fd(fd) };
438 assert!(file.set_len(0).is_ok());
440 let invalid_templates = vec!["foo", "barXX", "XXXXXXbaz", "whatXXXXXXever", "X"];
441 for t in invalid_templates {
442 let ptr = CString::new(t).unwrap().into_raw();
443 let fd = unsafe { libc::mkstemp(ptr) };
444 let _ = unsafe { CString::from_raw(ptr) };
445 // "On error, -1 is returned, and errno is set to
446 // indicate the error"
448 let e = std::io::Error::last_os_error();
449 assert_eq!(e.raw_os_error(), Some(libc::EINVAL));
450 assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput);
455 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
456 test_posix_fadvise();
458 test_posix_gettimeofday();
459 test_posix_mkstemp();
461 test_posix_realpath_alloc();
462 test_posix_realpath_noalloc();
463 test_posix_realpath_errors();
465 #[cfg(any(target_os = "linux"))]
466 test_sync_file_range();
468 test_mutex_libc_init_recursive();
469 test_mutex_libc_init_normal();
470 test_mutex_libc_init_errorcheck();
471 test_rwlock_libc_static_initializer();
473 #[cfg(any(target_os = "linux"))]
474 test_mutex_libc_static_initializer_recursive();
476 #[cfg(any(target_os = "linux"))]
477 test_prctl_thread_name();
479 test_thread_local_errno();
481 #[cfg(any(target_os = "linux"))]