// ignore-windows: No libc on Windows // compile-flags: -Zmiri-disable-isolation #![feature(rustc_private)] extern crate libc; #[cfg(target_os = "linux")] fn tmp() -> std::path::PathBuf { std::env::var("MIRI_TEMP").map(std::path::PathBuf::from).unwrap_or_else(|_| std::env::temp_dir()) } #[cfg(target_os = "linux")] fn test_posix_fadvise() { use std::convert::TryInto; use std::fs::{remove_file, File}; use std::io::Write; use std::os::unix::io::AsRawFd; let path = tmp().join("miri_test_libc.txt"); // Cleanup before test remove_file(&path).ok(); // Set up an open file let mut file = File::create(&path).unwrap(); let bytes = b"Hello, World!\n"; file.write(bytes).unwrap(); // Test calling posix_fadvise on a file. let result = unsafe { libc::posix_fadvise( file.as_raw_fd(), 0, bytes.len().try_into().unwrap(), libc::POSIX_FADV_DONTNEED, ) }; drop(file); remove_file(&path).unwrap(); assert_eq!(result, 0); } fn test_mutex_libc_init_recursive() { unsafe { let mut attr: libc::pthread_mutexattr_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutexattr_init(&mut attr as *mut _), 0); assert_eq!(libc::pthread_mutexattr_settype(&mut attr as *mut _, libc::PTHREAD_MUTEX_RECURSIVE), 0); let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mut attr as *mut _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), libc::EPERM); assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutexattr_destroy(&mut attr as *mut _), 0); } } fn test_mutex_libc_init_normal() { unsafe { let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutexattr_settype(&mut mutexattr as *mut _, 0x12345678), libc::EINVAL); assert_eq!(libc::pthread_mutexattr_settype(&mut mutexattr as *mut _, libc::PTHREAD_MUTEX_NORMAL), 0); let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), libc::EBUSY); assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0); } } fn test_mutex_libc_init_errorcheck() { unsafe { let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutexattr_settype(&mut mutexattr as *mut _, libc::PTHREAD_MUTEX_ERRORCHECK), 0); let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), libc::EBUSY); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), libc::EDEADLK); assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0); assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), libc::EPERM); assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0); } } // Only linux provides PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, // libc for macOS just has the default PTHREAD_MUTEX_INITIALIZER. #[cfg(target_os = "linux")] fn test_mutex_libc_static_initializer_recursive() { let mutex = std::cell::UnsafeCell::new(libc::PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP); unsafe { assert_eq!(libc::pthread_mutex_lock(mutex.get()), 0); assert_eq!(libc::pthread_mutex_trylock(mutex.get()), 0); assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0); assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0); assert_eq!(libc::pthread_mutex_trylock(mutex.get()), 0); assert_eq!(libc::pthread_mutex_lock(mutex.get()), 0); assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0); assert_eq!(libc::pthread_mutex_unlock(mutex.get()), 0); assert_eq!(libc::pthread_mutex_unlock(mutex.get()), libc::EPERM); assert_eq!(libc::pthread_mutex_destroy(mutex.get()), 0); } } // Testing the behavior of std::sync::RwLock does not fully exercise the pthread rwlock shims, we // need to go a layer deeper and test the behavior of the libc functions, because // std::sys::unix::rwlock::RWLock itself keeps track of write_locked and num_readers. fn test_rwlock_libc_static_initializer() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); unsafe { assert_eq!(libc::pthread_rwlock_rdlock(rw.get()), 0); assert_eq!(libc::pthread_rwlock_rdlock(rw.get()), 0); assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0); assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), 0); assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0); assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY); assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0); assert_eq!(libc::pthread_rwlock_wrlock(rw.get()), 0); assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), libc::EBUSY); assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY); assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0); assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), 0); assert_eq!(libc::pthread_rwlock_tryrdlock(rw.get()), libc::EBUSY); assert_eq!(libc::pthread_rwlock_trywrlock(rw.get()), libc::EBUSY); assert_eq!(libc::pthread_rwlock_unlock(rw.get()), 0); assert_eq!(libc::pthread_rwlock_destroy(rw.get()), 0); } } /// Test whether the `prctl` shim correctly sets the thread name. /// /// Note: `prctl` exists only on Linux. #[cfg(target_os = "linux")] fn test_prctl_thread_name() { use std::ffi::CString; use libc::c_long; unsafe { let mut buf = [255; 10]; assert_eq!(libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr() as c_long, 0 as c_long, 0 as c_long, 0 as c_long), 0); assert_eq!(b"\0", &buf); let thread_name = CString::new("hello").expect("CString::new failed"); assert_eq!(libc::prctl(libc::PR_SET_NAME, thread_name.as_ptr() as c_long, 0 as c_long, 0 as c_long, 0 as c_long), 0); let mut buf = [255; 6]; assert_eq!(libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr() as c_long, 0 as c_long, 0 as c_long, 0 as c_long), 0); assert_eq!(b"hello\0", &buf); let long_thread_name = CString::new("01234567890123456789").expect("CString::new failed"); assert_eq!(libc::prctl(libc::PR_SET_NAME, long_thread_name.as_ptr() as c_long, 0 as c_long, 0 as c_long, 0 as c_long), 0); let mut buf = [255; 16]; assert_eq!(libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr() as c_long, 0 as c_long, 0 as c_long, 0 as c_long), 0); assert_eq!(b"012345678901234\0", &buf); } } fn main() { #[cfg(target_os = "linux")] test_posix_fadvise(); test_mutex_libc_init_recursive(); test_mutex_libc_init_normal(); test_mutex_libc_init_errorcheck(); test_rwlock_libc_static_initializer(); #[cfg(target_os = "linux")] test_mutex_libc_static_initializer_recursive(); #[cfg(target_os = "linux")] test_prctl_thread_name(); }