Skip to content

Commit

Permalink
Auto merge of #3565 - RalfJung:libc, r=RalfJung
Browse files Browse the repository at this point in the history
re-organize libc tests

And share some more things across unices
  • Loading branch information
bors committed May 5, 2024
2 parents fdd9500 + 7aff1f4 commit b125e90
Show file tree
Hide file tree
Showing 14 changed files with 417 additions and 450 deletions.
8 changes: 4 additions & 4 deletions ci/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,11 @@ case $HOST_TARGET in
# Partially supported targets (tier 2)
VERY_BASIC="integer vec string btreemap" # common things we test on all of them (if they have std), requires no target-specific shims
BASIC="$VERY_BASIC hello hashmap alloc align" # ensures we have the shims for stdout and basic data structures
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
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
MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-misc libc-random fs env num_cpus
MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-misc libc-random fs env num_cpus
MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic
MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic pthread-sync
# TODO fix solaris stack guard
MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic pthread-sync libc-misc libc-random
# TODO fix solaris stack guard
# MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic pthread-sync
MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm
MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm
Expand Down
20 changes: 19 additions & 1 deletion src/shims/unix/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub fn is_dyn_sym(name: &str, target_os: &str) -> bool {
// well allow it in `dlsym`.
"signal" => true,
// needed at least on macOS to avoid file-based fallback in getrandom
"getentropy" => true,
"getentropy" | "getrandom" => true,
// Give specific OSes a chance to allow their symbols.
_ =>
match target_os {
Expand Down Expand Up @@ -632,6 +632,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.write_scalar(Scalar::from_i32(0), dest)?;
}
}
"getrandom" => {
// This function is non-standard but exists with the same signature and behavior on
// Linux, FreeBSD and Solaris/Illumos.
if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris") {
throw_unsup_format!(
"`getentropy` is not supported on {}",
this.tcx.sess.target.os
);
}
let [ptr, len, flags] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let len = this.read_target_usize(len)?;
let _flags = this.read_scalar(flags)?.to_i32()?;
// We ignore the flags, just always use the same PRNG / host RNG.
this.gen_random(ptr, len)?;
this.write_scalar(Scalar::from_target_usize(len, this), dest)?;
}

// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
// These shims are enabled only when the caller is in the standard library.
Expand Down
28 changes: 8 additions & 20 deletions src/shims/unix/freebsd/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let this = self.eval_context_mut();
match link_name.as_str() {
// Threading
"pthread_attr_get_np" if this.frame_in_std() => {
let [_thread, _attr] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
this.write_null(dest)?;
}
"pthread_set_name_np" => {
let [thread, name] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
Expand Down Expand Up @@ -75,27 +70,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}

// Miscellaneous
"getrandom" => {
let [ptr, len, flags] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let len = this.read_target_usize(len)?;
let _flags = this.read_scalar(flags)?.to_i32()?;
// flags on freebsd does not really matter
// in practice, GRND_RANDOM does not particularly draw from /dev/random
// since it is the same as to /dev/urandom.
// GRND_INSECURE is only an alias of GRND_NONBLOCK, which
// does not affect the RNG.
// https://man.freebsd.org/cgi/man.cgi?query=getrandom&sektion=2&n=1
this.gen_random(ptr, len)?;
this.write_scalar(Scalar::from_target_usize(len, this), dest)?;
}
"__error" => {
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let errno_place = this.last_error_place()?;
this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
}

// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
// These shims are enabled only when the caller is in the standard library.
"pthread_attr_get_np" if this.frame_in_std() => {
let [_thread, _attr] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
this.write_null(dest)?;
}

_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsJumping)
Expand Down
7 changes: 1 addition & 6 deletions src/shims/unix/linux/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use shims::unix::linux::mem::EvalContextExt as _;
use shims::unix::linux::sync::futex;

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

impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
Expand Down Expand Up @@ -140,11 +140,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}

// Miscellaneous
"getrandom" => {
let [ptr, len, flags] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
getrandom(this, ptr, len, flags, dest)?;
}
"mmap64" => {
let [addr, length, prot, flags, fd, offset] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
Expand Down
8 changes: 7 additions & 1 deletion src/shims/unix/solarish/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ pub fn is_dyn_sym(_name: &str) -> bool {

impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
#[allow(warnings)]
fn emulate_foreign_item_inner(
&mut self,
link_name: Symbol,
Expand All @@ -20,6 +19,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
match link_name.as_str() {
// Miscellaneous
"___errno" => {
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let errno_place = this.last_error_place()?;
this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
}

_ => return Ok(EmulateItemResult::NotSupported),
}
Ok(EmulateItemResult::NeedsJumping)
Expand Down
174 changes: 173 additions & 1 deletion tests/pass-dep/shims/libc-fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
#![feature(io_error_more)]
#![feature(io_error_uncategorized)]

use std::ffi::CString;
use std::ffi::{CStr, CString, OsString};
use std::fs::{canonicalize, remove_dir_all, remove_file, File};
use std::io::{Error, ErrorKind, Write};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;

#[path = "../../utils/mod.rs"]
Expand All @@ -27,6 +28,14 @@ fn main() {
#[cfg(target_os = "linux")]
test_o_tmpfile_flag();
test_posix_mkstemp();
test_posix_realpath_alloc();
test_posix_realpath_noalloc();
test_posix_realpath_errors();
#[cfg(target_os = "linux")]
test_posix_fadvise();
#[cfg(target_os = "linux")]
test_sync_file_range();
test_isatty();
}

/// Prepare: compute filename and make sure the file does not exist.
Expand Down Expand Up @@ -256,3 +265,166 @@ fn test_posix_mkstemp() {
assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput);
}
}

/// Test allocating variant of `realpath`.
fn test_posix_realpath_alloc() {
use std::os::unix::ffi::OsStrExt;
use std::os::unix::ffi::OsStringExt;

let buf;
let path = utils::tmp().join("miri_test_libc_posix_realpath_alloc");
let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed");

// Cleanup before test.
remove_file(&path).ok();
// Create file.
drop(File::create(&path).unwrap());
unsafe {
let r = libc::realpath(c_path.as_ptr(), std::ptr::null_mut());
assert!(!r.is_null());
buf = CStr::from_ptr(r).to_bytes().to_vec();
libc::free(r as *mut _);
}
let canonical = PathBuf::from(OsString::from_vec(buf));
assert_eq!(path.file_name(), canonical.file_name());

// Cleanup after test.
remove_file(&path).unwrap();
}

/// Test non-allocating variant of `realpath`.
fn test_posix_realpath_noalloc() {
use std::ffi::{CStr, CString};
use std::os::unix::ffi::OsStrExt;

let path = utils::tmp().join("miri_test_libc_posix_realpath_noalloc");
let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed");

let mut v = vec![0; libc::PATH_MAX as usize];

// Cleanup before test.
remove_file(&path).ok();
// Create file.
drop(File::create(&path).unwrap());
unsafe {
let r = libc::realpath(c_path.as_ptr(), v.as_mut_ptr());
assert!(!r.is_null());
}
let c = unsafe { CStr::from_ptr(v.as_ptr()) };
let canonical = PathBuf::from(c.to_str().expect("CStr to str"));

assert_eq!(path.file_name(), canonical.file_name());

// Cleanup after test.
remove_file(&path).unwrap();
}

/// Test failure cases for `realpath`.
fn test_posix_realpath_errors() {
use std::ffi::CString;
use std::io::ErrorKind;

// Test nonexistent path returns an error.
let c_path = CString::new("./nothing_to_see_here").expect("CString::new failed");
let r = unsafe { libc::realpath(c_path.as_ptr(), std::ptr::null_mut()) };
assert!(r.is_null());
let e = std::io::Error::last_os_error();
assert_eq!(e.raw_os_error(), Some(libc::ENOENT));
assert_eq!(e.kind(), ErrorKind::NotFound);
}

#[cfg(target_os = "linux")]
fn test_posix_fadvise() {
use std::io::Write;

let path = utils::tmp().join("miri_test_libc_posix_fadvise.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);
}

#[cfg(target_os = "linux")]
fn test_sync_file_range() {
use std::io::Write;

let path = utils::tmp().join("miri_test_libc_sync_file_range.txt");
// Cleanup before test.
remove_file(&path).ok();

// Write to a file.
let mut file = File::create(&path).unwrap();
let bytes = b"Hello, World!\n";
file.write(bytes).unwrap();

// Test calling sync_file_range on the file.
let result_1 = unsafe {
libc::sync_file_range(
file.as_raw_fd(),
0,
0,
libc::SYNC_FILE_RANGE_WAIT_BEFORE
| libc::SYNC_FILE_RANGE_WRITE
| libc::SYNC_FILE_RANGE_WAIT_AFTER,
)
};
drop(file);

// Test calling sync_file_range on a file opened for reading.
let file = File::open(&path).unwrap();
let result_2 = unsafe {
libc::sync_file_range(
file.as_raw_fd(),
0,
0,
libc::SYNC_FILE_RANGE_WAIT_BEFORE
| libc::SYNC_FILE_RANGE_WRITE
| libc::SYNC_FILE_RANGE_WAIT_AFTER,
)
};
drop(file);

remove_file(&path).unwrap();
assert_eq!(result_1, 0);
assert_eq!(result_2, 0);
}

fn test_isatty() {
// Testing whether our isatty shim returns the right value would require controlling whether
// these streams are actually TTYs, which is hard.
// For now, we just check that these calls are supported at all.
unsafe {
libc::isatty(libc::STDIN_FILENO);
libc::isatty(libc::STDOUT_FILENO);
libc::isatty(libc::STDERR_FILENO);

// But when we open a file, it is definitely not a TTY.
let path = utils::tmp().join("notatty.txt");
// Cleanup before test.
remove_file(&path).ok();
let file = File::create(&path).unwrap();

assert_eq!(libc::isatty(file.as_raw_fd()), 0);
assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::ENOTTY);

// Cleanup after test.
drop(file);
remove_file(&path).unwrap();
}
}
13 changes: 0 additions & 13 deletions tests/pass-dep/shims/libc-getentropy.rs

This file was deleted.

41 changes: 0 additions & 41 deletions tests/pass-dep/shims/libc-getrandom-without-isolation.rs

This file was deleted.

Loading

0 comments on commit b125e90

Please sign in to comment.