Skip to content

Commit 19a5d47

Browse files
committed
Auto merge of rust-lang#3565 - RalfJung:libc, r=RalfJung
re-organize libc tests And share some more things across unices
2 parents e1b5e55 + f301840 commit 19a5d47

14 files changed

+418
-451
lines changed

src/tools/miri/ci/ci.sh

+5-5
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,12 @@ case $HOST_TARGET in
143143
# Partially supported targets (tier 2)
144144
VERY_BASIC="integer vec string btreemap" # common things we test on all of them (if they have std), requires no target-specific shims
145145
BASIC="$VERY_BASIC hello hashmap alloc align" # ensures we have the shims for stdout and basic data structures
146-
MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-getentropy libc-getrandom libc-misc fs env num_cpus
147-
MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-getentropy libc-getrandom libc-misc fs env num_cpus
148-
MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic
149-
MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync
150-
# TODO fix solaris stack guard
146+
MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-misc libc-random libc-time fs env num_cpus
147+
MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-misc libc-random libc-time fs env num_cpus
148+
MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-misc libc-random
149+
# TODO fix solaris stack guard
151150
# MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic pthread-sync
151+
MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic
152152
MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm
153153
MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm
154154
MIRI_TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std

src/tools/miri/src/shims/unix/foreign_items.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub fn is_dyn_sym(name: &str, target_os: &str) -> bool {
2323
// well allow it in `dlsym`.
2424
"signal" => true,
2525
// needed at least on macOS to avoid file-based fallback in getrandom
26-
"getentropy" => true,
26+
"getentropy" | "getrandom" => true,
2727
// Give specific OSes a chance to allow their symbols.
2828
_ =>
2929
match target_os {
@@ -632,6 +632,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
632632
this.write_scalar(Scalar::from_i32(0), dest)?;
633633
}
634634
}
635+
"getrandom" => {
636+
// This function is non-standard but exists with the same signature and behavior on
637+
// Linux, FreeBSD and Solaris/Illumos.
638+
if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris") {
639+
throw_unsup_format!(
640+
"`getentropy` is not supported on {}",
641+
this.tcx.sess.target.os
642+
);
643+
}
644+
let [ptr, len, flags] =
645+
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
646+
let ptr = this.read_pointer(ptr)?;
647+
let len = this.read_target_usize(len)?;
648+
let _flags = this.read_scalar(flags)?.to_i32()?;
649+
// We ignore the flags, just always use the same PRNG / host RNG.
650+
this.gen_random(ptr, len)?;
651+
this.write_scalar(Scalar::from_target_usize(len, this), dest)?;
652+
}
635653

636654
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
637655
// These shims are enabled only when the caller is in the standard library.

src/tools/miri/src/shims/unix/freebsd/foreign_items.rs

+8-20
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
2020
let this = self.eval_context_mut();
2121
match link_name.as_str() {
2222
// Threading
23-
"pthread_attr_get_np" if this.frame_in_std() => {
24-
let [_thread, _attr] =
25-
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
26-
this.write_null(dest)?;
27-
}
2823
"pthread_set_name_np" => {
2924
let [thread, name] =
3025
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
@@ -75,27 +70,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
7570
}
7671

7772
// Miscellaneous
78-
"getrandom" => {
79-
let [ptr, len, flags] =
80-
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
81-
let ptr = this.read_pointer(ptr)?;
82-
let len = this.read_target_usize(len)?;
83-
let _flags = this.read_scalar(flags)?.to_i32()?;
84-
// flags on freebsd does not really matter
85-
// in practice, GRND_RANDOM does not particularly draw from /dev/random
86-
// since it is the same as to /dev/urandom.
87-
// GRND_INSECURE is only an alias of GRND_NONBLOCK, which
88-
// does not affect the RNG.
89-
// https://man.freebsd.org/cgi/man.cgi?query=getrandom&sektion=2&n=1
90-
this.gen_random(ptr, len)?;
91-
this.write_scalar(Scalar::from_target_usize(len, this), dest)?;
92-
}
9373
"__error" => {
9474
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
9575
let errno_place = this.last_error_place()?;
9676
this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
9777
}
9878

79+
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
80+
// These shims are enabled only when the caller is in the standard library.
81+
"pthread_attr_get_np" if this.frame_in_std() => {
82+
let [_thread, _attr] =
83+
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
84+
this.write_null(dest)?;
85+
}
86+
9987
_ => return Ok(EmulateItemResult::NotSupported),
10088
}
10189
Ok(EmulateItemResult::NeedsJumping)

src/tools/miri/src/shims/unix/linux/foreign_items.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use shims::unix::linux::mem::EvalContextExt as _;
1111
use shims::unix::linux::sync::futex;
1212

1313
pub fn is_dyn_sym(name: &str) -> bool {
14-
matches!(name, "getrandom" | "statx")
14+
matches!(name, "statx")
1515
}
1616

1717
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
@@ -140,11 +140,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
140140
}
141141

142142
// Miscellaneous
143-
"getrandom" => {
144-
let [ptr, len, flags] =
145-
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
146-
getrandom(this, ptr, len, flags, dest)?;
147-
}
148143
"mmap64" => {
149144
let [addr, length, prot, flags, fd, offset] =
150145
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;

src/tools/miri/src/shims/unix/solarish/foreign_items.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ pub fn is_dyn_sym(_name: &str) -> bool {
1010

1111
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
1212
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
13-
#[allow(warnings)]
1413
fn emulate_foreign_item_inner(
1514
&mut self,
1615
link_name: Symbol,
@@ -20,6 +19,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
2019
) -> InterpResult<'tcx, EmulateItemResult> {
2120
let this = self.eval_context_mut();
2221
match link_name.as_str() {
22+
// Miscellaneous
23+
"___errno" => {
24+
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
25+
let errno_place = this.last_error_place()?;
26+
this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
27+
}
28+
2329
_ => return Ok(EmulateItemResult::NotSupported),
2430
}
2531
Ok(EmulateItemResult::NeedsJumping)

src/tools/miri/tests/pass-dep/shims/libc-fs.rs

+173-1
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
#![feature(io_error_more)]
55
#![feature(io_error_uncategorized)]
66

7-
use std::ffi::CString;
7+
use std::ffi::{CStr, CString, OsString};
88
use std::fs::{canonicalize, remove_dir_all, remove_file, File};
99
use std::io::{Error, ErrorKind, Write};
1010
use std::os::unix::ffi::OsStrExt;
11+
use std::os::unix::io::AsRawFd;
1112
use std::path::PathBuf;
1213

1314
#[path = "../../utils/mod.rs"]
@@ -27,6 +28,14 @@ fn main() {
2728
#[cfg(target_os = "linux")]
2829
test_o_tmpfile_flag();
2930
test_posix_mkstemp();
31+
test_posix_realpath_alloc();
32+
test_posix_realpath_noalloc();
33+
test_posix_realpath_errors();
34+
#[cfg(target_os = "linux")]
35+
test_posix_fadvise();
36+
#[cfg(target_os = "linux")]
37+
test_sync_file_range();
38+
test_isatty();
3039
}
3140

3241
/// Prepare: compute filename and make sure the file does not exist.
@@ -256,3 +265,166 @@ fn test_posix_mkstemp() {
256265
assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput);
257266
}
258267
}
268+
269+
/// Test allocating variant of `realpath`.
270+
fn test_posix_realpath_alloc() {
271+
use std::os::unix::ffi::OsStrExt;
272+
use std::os::unix::ffi::OsStringExt;
273+
274+
let buf;
275+
let path = utils::tmp().join("miri_test_libc_posix_realpath_alloc");
276+
let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed");
277+
278+
// Cleanup before test.
279+
remove_file(&path).ok();
280+
// Create file.
281+
drop(File::create(&path).unwrap());
282+
unsafe {
283+
let r = libc::realpath(c_path.as_ptr(), std::ptr::null_mut());
284+
assert!(!r.is_null());
285+
buf = CStr::from_ptr(r).to_bytes().to_vec();
286+
libc::free(r as *mut _);
287+
}
288+
let canonical = PathBuf::from(OsString::from_vec(buf));
289+
assert_eq!(path.file_name(), canonical.file_name());
290+
291+
// Cleanup after test.
292+
remove_file(&path).unwrap();
293+
}
294+
295+
/// Test non-allocating variant of `realpath`.
296+
fn test_posix_realpath_noalloc() {
297+
use std::ffi::{CStr, CString};
298+
use std::os::unix::ffi::OsStrExt;
299+
300+
let path = utils::tmp().join("miri_test_libc_posix_realpath_noalloc");
301+
let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed");
302+
303+
let mut v = vec![0; libc::PATH_MAX as usize];
304+
305+
// Cleanup before test.
306+
remove_file(&path).ok();
307+
// Create file.
308+
drop(File::create(&path).unwrap());
309+
unsafe {
310+
let r = libc::realpath(c_path.as_ptr(), v.as_mut_ptr());
311+
assert!(!r.is_null());
312+
}
313+
let c = unsafe { CStr::from_ptr(v.as_ptr()) };
314+
let canonical = PathBuf::from(c.to_str().expect("CStr to str"));
315+
316+
assert_eq!(path.file_name(), canonical.file_name());
317+
318+
// Cleanup after test.
319+
remove_file(&path).unwrap();
320+
}
321+
322+
/// Test failure cases for `realpath`.
323+
fn test_posix_realpath_errors() {
324+
use std::ffi::CString;
325+
use std::io::ErrorKind;
326+
327+
// Test nonexistent path returns an error.
328+
let c_path = CString::new("./nothing_to_see_here").expect("CString::new failed");
329+
let r = unsafe { libc::realpath(c_path.as_ptr(), std::ptr::null_mut()) };
330+
assert!(r.is_null());
331+
let e = std::io::Error::last_os_error();
332+
assert_eq!(e.raw_os_error(), Some(libc::ENOENT));
333+
assert_eq!(e.kind(), ErrorKind::NotFound);
334+
}
335+
336+
#[cfg(target_os = "linux")]
337+
fn test_posix_fadvise() {
338+
use std::io::Write;
339+
340+
let path = utils::tmp().join("miri_test_libc_posix_fadvise.txt");
341+
// Cleanup before test
342+
remove_file(&path).ok();
343+
344+
// Set up an open file
345+
let mut file = File::create(&path).unwrap();
346+
let bytes = b"Hello, World!\n";
347+
file.write(bytes).unwrap();
348+
349+
// Test calling posix_fadvise on a file.
350+
let result = unsafe {
351+
libc::posix_fadvise(
352+
file.as_raw_fd(),
353+
0,
354+
bytes.len().try_into().unwrap(),
355+
libc::POSIX_FADV_DONTNEED,
356+
)
357+
};
358+
drop(file);
359+
remove_file(&path).unwrap();
360+
assert_eq!(result, 0);
361+
}
362+
363+
#[cfg(target_os = "linux")]
364+
fn test_sync_file_range() {
365+
use std::io::Write;
366+
367+
let path = utils::tmp().join("miri_test_libc_sync_file_range.txt");
368+
// Cleanup before test.
369+
remove_file(&path).ok();
370+
371+
// Write to a file.
372+
let mut file = File::create(&path).unwrap();
373+
let bytes = b"Hello, World!\n";
374+
file.write(bytes).unwrap();
375+
376+
// Test calling sync_file_range on the file.
377+
let result_1 = unsafe {
378+
libc::sync_file_range(
379+
file.as_raw_fd(),
380+
0,
381+
0,
382+
libc::SYNC_FILE_RANGE_WAIT_BEFORE
383+
| libc::SYNC_FILE_RANGE_WRITE
384+
| libc::SYNC_FILE_RANGE_WAIT_AFTER,
385+
)
386+
};
387+
drop(file);
388+
389+
// Test calling sync_file_range on a file opened for reading.
390+
let file = File::open(&path).unwrap();
391+
let result_2 = unsafe {
392+
libc::sync_file_range(
393+
file.as_raw_fd(),
394+
0,
395+
0,
396+
libc::SYNC_FILE_RANGE_WAIT_BEFORE
397+
| libc::SYNC_FILE_RANGE_WRITE
398+
| libc::SYNC_FILE_RANGE_WAIT_AFTER,
399+
)
400+
};
401+
drop(file);
402+
403+
remove_file(&path).unwrap();
404+
assert_eq!(result_1, 0);
405+
assert_eq!(result_2, 0);
406+
}
407+
408+
fn test_isatty() {
409+
// Testing whether our isatty shim returns the right value would require controlling whether
410+
// these streams are actually TTYs, which is hard.
411+
// For now, we just check that these calls are supported at all.
412+
unsafe {
413+
libc::isatty(libc::STDIN_FILENO);
414+
libc::isatty(libc::STDOUT_FILENO);
415+
libc::isatty(libc::STDERR_FILENO);
416+
417+
// But when we open a file, it is definitely not a TTY.
418+
let path = utils::tmp().join("notatty.txt");
419+
// Cleanup before test.
420+
remove_file(&path).ok();
421+
let file = File::create(&path).unwrap();
422+
423+
assert_eq!(libc::isatty(file.as_raw_fd()), 0);
424+
assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::ENOTTY);
425+
426+
// Cleanup after test.
427+
drop(file);
428+
remove_file(&path).unwrap();
429+
}
430+
}

src/tools/miri/tests/pass-dep/shims/libc-getentropy.rs

-13
This file was deleted.

src/tools/miri/tests/pass-dep/shims/libc-getrandom-without-isolation.rs

-41
This file was deleted.

0 commit comments

Comments
 (0)