]> git.lizzy.rs Git - rust.git/blob - tests/pass/libc.rs
Add `mkstemp` shim for unix
[rust.git] / tests / pass / libc.rs
1 //@ignore-target-windows: No libc on Windows
2 //@compile-flags: -Zmiri-disable-isolation
3 #![feature(io_error_more)]
4 #![feature(rustc_private)]
5
6 use std::fs::{remove_file, File};
7 use std::os::unix::io::AsRawFd;
8 use std::path::PathBuf;
9
10 fn tmp() -> PathBuf {
11     std::env::var("MIRI_TEMP")
12         .map(|tmp| {
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("\\", "/"));
18         })
19         .unwrap_or_else(|_| std::env::temp_dir())
20 }
21
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;
28
29     let buf;
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");
32
33     // Cleanup before test.
34     remove_file(&path).ok();
35     // Create file.
36     drop(File::create(&path).unwrap());
37     unsafe {
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 _);
42     }
43     let canonical = PathBuf::from(OsString::from_vec(buf));
44     assert_eq!(path.file_name(), canonical.file_name());
45
46     // Cleanup after test.
47     remove_file(&path).unwrap();
48 }
49
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;
54
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");
57
58     let mut v = vec![0; libc::PATH_MAX as usize];
59
60     // Cleanup before test.
61     remove_file(&path).ok();
62     // Create file.
63     drop(File::create(&path).unwrap());
64     unsafe {
65         let r = libc::realpath(c_path.as_ptr(), v.as_mut_ptr());
66         assert!(!r.is_null());
67     }
68     let c = unsafe { CStr::from_ptr(v.as_ptr()) };
69     let canonical = PathBuf::from(c.to_str().expect("CStr to str"));
70
71     assert_eq!(path.file_name(), canonical.file_name());
72
73     // Cleanup after test.
74     remove_file(&path).unwrap();
75 }
76
77 /// Test failure cases for `realpath`.
78 fn test_posix_realpath_errors() {
79     use std::ffi::CString;
80     use std::io::ErrorKind;
81
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()) };
85     assert!(r.is_null());
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);
89 }
90
91 #[cfg(any(target_os = "linux", target_os = "freebsd"))]
92 fn test_posix_fadvise() {
93     use std::convert::TryInto;
94     use std::io::Write;
95
96     let path = tmp().join("miri_test_libc_posix_fadvise.txt");
97     // Cleanup before test
98     remove_file(&path).ok();
99
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();
104
105     // Test calling posix_fadvise on a file.
106     let result = unsafe {
107         libc::posix_fadvise(
108             file.as_raw_fd(),
109             0,
110             bytes.len().try_into().unwrap(),
111             libc::POSIX_FADV_DONTNEED,
112         )
113     };
114     drop(file);
115     remove_file(&path).unwrap();
116     assert_eq!(result, 0);
117 }
118
119 #[cfg(any(target_os = "linux"))]
120 fn test_sync_file_range() {
121     use std::io::Write;
122
123     let path = tmp().join("miri_test_libc_sync_file_range.txt");
124     // Cleanup before test.
125     remove_file(&path).ok();
126
127     // Write to a file.
128     let mut file = File::create(&path).unwrap();
129     let bytes = b"Hello, World!\n";
130     file.write(bytes).unwrap();
131
132     // Test calling sync_file_range on the file.
133     let result_1 = unsafe {
134         libc::sync_file_range(
135             file.as_raw_fd(),
136             0,
137             0,
138             libc::SYNC_FILE_RANGE_WAIT_BEFORE
139                 | libc::SYNC_FILE_RANGE_WRITE
140                 | libc::SYNC_FILE_RANGE_WAIT_AFTER,
141         )
142     };
143     drop(file);
144
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(
149             file.as_raw_fd(),
150             0,
151             0,
152             libc::SYNC_FILE_RANGE_WAIT_BEFORE
153                 | libc::SYNC_FILE_RANGE_WRITE
154                 | libc::SYNC_FILE_RANGE_WAIT_AFTER,
155         )
156     };
157     drop(file);
158
159     remove_file(&path).unwrap();
160     assert_eq!(result_1, 0);
161     assert_eq!(result_2, 0);
162 }
163
164 fn test_mutex_libc_init_recursive() {
165     unsafe {
166         let mut attr: libc::pthread_mutexattr_t = std::mem::zeroed();
167         assert_eq!(libc::pthread_mutexattr_init(&mut attr as *mut _), 0);
168         assert_eq!(
169             libc::pthread_mutexattr_settype(&mut attr as *mut _, libc::PTHREAD_MUTEX_RECURSIVE),
170             0,
171         );
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);
185     }
186 }
187
188 fn test_mutex_libc_init_normal() {
189     unsafe {
190         let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed();
191         assert_eq!(
192             libc::pthread_mutexattr_settype(&mut mutexattr as *mut _, 0x12345678),
193             libc::EINVAL,
194         );
195         assert_eq!(
196             libc::pthread_mutexattr_settype(&mut mutexattr as *mut _, libc::PTHREAD_MUTEX_NORMAL),
197             0,
198         );
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);
207     }
208 }
209
210 fn test_mutex_libc_init_errorcheck() {
211     unsafe {
212         let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed();
213         assert_eq!(
214             libc::pthread_mutexattr_settype(
215                 &mut mutexattr as *mut _,
216                 libc::PTHREAD_MUTEX_ERRORCHECK,
217             ),
218             0,
219         );
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);
230     }
231 }
232
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);
238     unsafe {
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);
249     }
250 }
251
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);
257     unsafe {
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);
265
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);
270
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);
275
276         assert_eq!(libc::pthread_rwlock_destroy(rw.get()), 0);
277     }
278 }
279
280 /// Test whether the `prctl` shim correctly sets the thread name.
281 ///
282 /// Note: `prctl` exists only on Linux.
283 #[cfg(any(target_os = "linux"))]
284 fn test_prctl_thread_name() {
285     use libc::c_long;
286     use std::ffi::CString;
287     unsafe {
288         let mut buf = [255; 10];
289         assert_eq!(
290             libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr(), 0 as c_long, 0 as c_long, 0 as c_long),
291             0,
292         );
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");
296         assert_eq!(
297             libc::prctl(
298                 libc::PR_SET_NAME,
299                 thread_name.as_ptr(),
300                 0 as c_long,
301                 0 as c_long,
302                 0 as c_long,
303             ),
304             0,
305         );
306         let mut buf = [255; 6];
307         assert_eq!(
308             libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr(), 0 as c_long, 0 as c_long, 0 as c_long),
309             0,
310         );
311         assert_eq!(b"hello\0", &buf);
312         let long_thread_name = CString::new("01234567890123456789").expect("CString::new failed");
313         assert_eq!(
314             libc::prctl(
315                 libc::PR_SET_NAME,
316                 long_thread_name.as_ptr(),
317                 0 as c_long,
318                 0 as c_long,
319                 0 as c_long,
320             ),
321             0,
322         );
323         let mut buf = [255; 16];
324         assert_eq!(
325             libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr(), 0 as c_long, 0 as c_long, 0 as c_long),
326             0,
327         );
328         assert_eq!(b"012345678901234\0", &buf);
329     }
330 }
331
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;
338
339     unsafe {
340         *__errno_location() = 0xBEEF;
341         std::thread::spawn(|| {
342             assert_eq!(*__errno_location(), 0);
343             *__errno_location() = 0xBAD1DEA;
344             assert_eq!(*__errno_location(), 0xBAD1DEA);
345         })
346         .join()
347         .unwrap();
348         assert_eq!(*__errno_location(), 0xBEEF);
349     }
350 }
351
352 /// Tests whether clock support exists at all
353 #[cfg(any(target_os = "linux"))]
354 fn test_clocks() {
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);
364 }
365
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.
376
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);
384 }
385
386 fn test_isatty() {
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.
390     unsafe {
391         libc::isatty(libc::STDIN_FILENO);
392         libc::isatty(libc::STDOUT_FILENO);
393         libc::isatty(libc::STDERR_FILENO);
394
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();
400
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);
403
404         // Cleanup after test.
405         drop(file);
406         remove_file(&path).unwrap();
407     }
408 }
409
410 fn test_posix_mkstemp() {
411     use std::ffi::CString;
412     use std::ffi::OsStr;
413     use std::os::unix::ffi::OsStrExt;
414     use std::os::unix::io::FromRawFd;
415     use std::path::Path;
416
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) };
426     assert!(fd > 0);
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);
433     assert_eq!(
434         name.chars().skip(3).filter(char::is_ascii_alphanumeric).collect::<Vec<char>>().len(),
435         6
436     );
437     let file = unsafe { File::from_raw_fd(fd) };
438     assert!(file.set_len(0).is_ok());
439
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"
447         assert_eq!(fd, -1);
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);
451     }
452 }
453
454 fn main() {
455     #[cfg(any(target_os = "linux", target_os = "freebsd"))]
456     test_posix_fadvise();
457
458     test_posix_gettimeofday();
459     test_posix_mkstemp();
460
461     test_posix_realpath_alloc();
462     test_posix_realpath_noalloc();
463     test_posix_realpath_errors();
464
465     #[cfg(any(target_os = "linux"))]
466     test_sync_file_range();
467
468     test_mutex_libc_init_recursive();
469     test_mutex_libc_init_normal();
470     test_mutex_libc_init_errorcheck();
471     test_rwlock_libc_static_initializer();
472
473     #[cfg(any(target_os = "linux"))]
474     test_mutex_libc_static_initializer_recursive();
475
476     #[cfg(any(target_os = "linux"))]
477     test_prctl_thread_name();
478
479     test_thread_local_errno();
480
481     #[cfg(any(target_os = "linux"))]
482     test_clocks();
483
484     test_isatty();
485 }