Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

re-organize libc tests #3565

Merged
merged 3 commits into from
May 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions ci/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,12 @@ 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=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 concurrency/simple pthread-sync
# TODO fix solaris stack guard
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
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
MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple 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=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic
MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm
MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm
MIRI_TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std
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