Auto merge of #3565 - RalfJung:libc, r=RalfJung
re-organize libc tests And share some more things across unices
This commit is contained in:
commit
19a5d475f2
@ -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
|
||||
|
@ -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 {
|
||||
@ -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.
|
||||
|
@ -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)?;
|
||||
@ -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)
|
||||
|
@ -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> {}
|
||||
@ -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)?;
|
||||
|
@ -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,
|
||||
@ -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)
|
||||
|
@ -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"]
|
||||
@ -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.
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +0,0 @@
|
||||
//@ignore-target-windows: no libc
|
||||
|
||||
use libc::getentropy;
|
||||
|
||||
fn main() {
|
||||
let mut buf1 = [0u8; 256];
|
||||
let mut buf2 = [0u8; 257];
|
||||
unsafe {
|
||||
assert_eq!(getentropy(buf1.as_mut_ptr() as *mut libc::c_void, buf1.len()), 0);
|
||||
assert_eq!(getentropy(buf2.as_mut_ptr() as *mut libc::c_void, buf2.len()), -1);
|
||||
assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::EIO);
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
//@only-target-linux
|
||||
//@compile-flags: -Zmiri-disable-isolation
|
||||
|
||||
use std::ptr;
|
||||
|
||||
fn main() {
|
||||
let mut buf = [0u8; 5];
|
||||
unsafe {
|
||||
assert_eq!(
|
||||
libc::syscall(
|
||||
libc::SYS_getrandom,
|
||||
ptr::null_mut::<libc::c_void>(),
|
||||
0 as libc::size_t,
|
||||
0 as libc::c_uint,
|
||||
),
|
||||
0,
|
||||
);
|
||||
assert_eq!(
|
||||
libc::syscall(
|
||||
libc::SYS_getrandom,
|
||||
buf.as_mut_ptr() as *mut libc::c_void,
|
||||
5 as libc::size_t,
|
||||
0 as libc::c_uint,
|
||||
),
|
||||
5,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
libc::getrandom(ptr::null_mut::<libc::c_void>(), 0 as libc::size_t, 0 as libc::c_uint),
|
||||
0,
|
||||
);
|
||||
assert_eq!(
|
||||
libc::getrandom(
|
||||
buf.as_mut_ptr() as *mut libc::c_void,
|
||||
5 as libc::size_t,
|
||||
0 as libc::c_uint,
|
||||
),
|
||||
5,
|
||||
);
|
||||
}
|
||||
}
|
@ -1,158 +1,16 @@
|
||||
//@ignore-target-windows: No libc on Windows
|
||||
//@compile-flags: -Zmiri-disable-isolation
|
||||
#![feature(io_error_more)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::fs::{remove_file, File};
|
||||
use std::mem::transmute;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[path = "../../utils/mod.rs"]
|
||||
mod utils;
|
||||
|
||||
/// Test allocating variant of `realpath`.
|
||||
fn test_posix_realpath_alloc() {
|
||||
use std::ffi::OsString;
|
||||
use std::ffi::{CStr, CString};
|
||||
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);
|
||||
}
|
||||
use std::mem::{self, transmute};
|
||||
use std::ptr;
|
||||
|
||||
/// Tests whether each thread has its own `__errno_location`.
|
||||
fn test_thread_local_errno() {
|
||||
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
|
||||
use libc::___errno as __errno_location;
|
||||
#[cfg(target_os = "linux")]
|
||||
use libc::__errno_location;
|
||||
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
|
||||
@ -171,116 +29,6 @@ fn test_thread_local_errno() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests whether clock support exists at all
|
||||
fn test_clocks() {
|
||||
let mut tp = std::mem::MaybeUninit::<libc::timespec>::uninit();
|
||||
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, tp.as_mut_ptr()) };
|
||||
assert_eq!(is_error, 0);
|
||||
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, tp.as_mut_ptr()) };
|
||||
assert_eq!(is_error, 0);
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
{
|
||||
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME_COARSE, tp.as_mut_ptr()) };
|
||||
assert_eq!(is_error, 0);
|
||||
let is_error =
|
||||
unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_COARSE, tp.as_mut_ptr()) };
|
||||
assert_eq!(is_error, 0);
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_UPTIME_RAW, tp.as_mut_ptr()) };
|
||||
assert_eq!(is_error, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_posix_gettimeofday() {
|
||||
let mut tp = std::mem::MaybeUninit::<libc::timeval>::uninit();
|
||||
let tz = std::ptr::null_mut::<libc::timezone>();
|
||||
#[cfg(target_os = "macos")] // `tz` has a different type on macOS
|
||||
let tz = tz as *mut libc::c_void;
|
||||
let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz) };
|
||||
assert_eq!(is_error, 0);
|
||||
let tv = unsafe { tp.assume_init() };
|
||||
assert!(tv.tv_sec > 0);
|
||||
assert!(tv.tv_usec >= 0); // Theoretically this could be 0.
|
||||
|
||||
// Test that non-null tz returns an error.
|
||||
let mut tz = std::mem::MaybeUninit::<libc::timezone>::uninit();
|
||||
let tz_ptr = tz.as_mut_ptr();
|
||||
#[cfg(target_os = "macos")] // `tz` has a different type on macOS
|
||||
let tz_ptr = tz_ptr as *mut libc::c_void;
|
||||
let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr) };
|
||||
assert_eq!(is_error, -1);
|
||||
}
|
||||
|
||||
fn test_localtime_r() {
|
||||
use std::ffi::CStr;
|
||||
use std::{env, ptr};
|
||||
|
||||
// Set timezone to GMT.
|
||||
let key = "TZ";
|
||||
env::set_var(key, "GMT");
|
||||
|
||||
const TIME_SINCE_EPOCH: libc::time_t = 1712475836;
|
||||
let custom_time_ptr = &TIME_SINCE_EPOCH;
|
||||
let mut tm = libc::tm {
|
||||
tm_sec: 0,
|
||||
tm_min: 0,
|
||||
tm_hour: 0,
|
||||
tm_mday: 0,
|
||||
tm_mon: 0,
|
||||
tm_year: 0,
|
||||
tm_wday: 0,
|
||||
tm_yday: 0,
|
||||
tm_isdst: 0,
|
||||
tm_gmtoff: 0,
|
||||
tm_zone: std::ptr::null_mut::<libc::c_char>(),
|
||||
};
|
||||
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
|
||||
|
||||
assert_eq!(tm.tm_sec, 56);
|
||||
assert_eq!(tm.tm_min, 43);
|
||||
assert_eq!(tm.tm_hour, 7);
|
||||
assert_eq!(tm.tm_mday, 7);
|
||||
assert_eq!(tm.tm_mon, 3);
|
||||
assert_eq!(tm.tm_year, 124);
|
||||
assert_eq!(tm.tm_wday, 0);
|
||||
assert_eq!(tm.tm_yday, 97);
|
||||
assert_eq!(tm.tm_isdst, -1);
|
||||
assert_eq!(tm.tm_gmtoff, 0);
|
||||
unsafe { assert_eq!(CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00") };
|
||||
|
||||
// The returned value is the pointer passed in.
|
||||
assert!(ptr::eq(res, &mut tm));
|
||||
|
||||
//Remove timezone setting.
|
||||
env::remove_var(key);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
fn test_memcpy() {
|
||||
unsafe {
|
||||
let src = [1i8, 2, 3];
|
||||
@ -306,7 +54,7 @@ fn test_memcpy() {
|
||||
libc::memcpy(
|
||||
&mut dest as *mut i32 as *mut libc::c_void,
|
||||
&src as *const i32 as *const libc::c_void,
|
||||
std::mem::size_of::<i32>(),
|
||||
mem::size_of::<i32>(),
|
||||
);
|
||||
assert_eq!(dest, src);
|
||||
}
|
||||
@ -317,7 +65,7 @@ fn test_memcpy() {
|
||||
libc::memcpy(
|
||||
&mut dest as *mut Option<i32> as *mut libc::c_void,
|
||||
&src as *const Option<i32> as *const libc::c_void,
|
||||
std::mem::size_of::<Option<i32>>(),
|
||||
mem::size_of::<Option<i32>>(),
|
||||
);
|
||||
assert_eq!(dest, src);
|
||||
}
|
||||
@ -328,7 +76,7 @@ fn test_memcpy() {
|
||||
libc::memcpy(
|
||||
&mut dest as *mut &'static i32 as *mut libc::c_void,
|
||||
&src as *const &'static i32 as *const libc::c_void,
|
||||
std::mem::size_of::<&'static i32>(),
|
||||
mem::size_of::<&'static i32>(),
|
||||
);
|
||||
assert_eq!(*dest, 123);
|
||||
}
|
||||
@ -388,7 +136,7 @@ fn test_dlsym() {
|
||||
assert_eq!(errno, libc::EBADF);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
#[cfg(not(any(target_os = "macos", target_os = "illumos")))]
|
||||
fn test_reallocarray() {
|
||||
unsafe {
|
||||
let mut p = libc::reallocarray(std::ptr::null_mut(), 4096, 2);
|
||||
@ -401,33 +149,94 @@ fn test_reallocarray() {
|
||||
}
|
||||
}
|
||||
|
||||
fn test_memalign() {
|
||||
// A normal allocation.
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 8;
|
||||
let size = 64;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
assert!(!ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
ptr.cast::<u8>().write_bytes(1, size);
|
||||
libc::free(ptr);
|
||||
}
|
||||
|
||||
// Align > size.
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 64;
|
||||
let size = 8;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
assert!(!ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
ptr.cast::<u8>().write_bytes(1, size);
|
||||
libc::free(ptr);
|
||||
}
|
||||
|
||||
// Size not multiple of align
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 16;
|
||||
let size = 31;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
assert!(!ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
ptr.cast::<u8>().write_bytes(1, size);
|
||||
libc::free(ptr);
|
||||
}
|
||||
|
||||
// Size == 0
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 64;
|
||||
let size = 0;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
// We are not required to return null if size == 0, but we currently do.
|
||||
// It's fine to remove this assert if we start returning non-null pointers.
|
||||
assert!(ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
// Regardless of what we return, it must be `free`able.
|
||||
libc::free(ptr);
|
||||
}
|
||||
|
||||
// Non-power of 2 align
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::without_provenance_mut(0x1234567);
|
||||
let align = 15;
|
||||
let size = 8;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), libc::EINVAL);
|
||||
// The pointer is not modified on failure, posix_memalign(3) says:
|
||||
// > On Linux (and other systems), posix_memalign() does not modify memptr on failure.
|
||||
// > A requirement standardizing this behavior was added in POSIX.1-2008 TC2.
|
||||
assert_eq!(ptr.addr(), 0x1234567);
|
||||
}
|
||||
|
||||
// Too small align (smaller than ptr)
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::without_provenance_mut(0x1234567);
|
||||
let align = std::mem::size_of::<usize>() / 2;
|
||||
let size = 8;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), libc::EINVAL);
|
||||
// The pointer is not modified on failure, posix_memalign(3) says:
|
||||
// > On Linux (and other systems), posix_memalign() does not modify memptr on failure.
|
||||
// > A requirement standardizing this behavior was added in POSIX.1-2008 TC2.
|
||||
assert_eq!(ptr.addr(), 0x1234567);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_posix_gettimeofday();
|
||||
|
||||
test_posix_realpath_alloc();
|
||||
test_posix_realpath_noalloc();
|
||||
test_posix_realpath_errors();
|
||||
|
||||
test_thread_local_errno();
|
||||
test_localtime_r();
|
||||
|
||||
test_isatty();
|
||||
|
||||
test_clocks();
|
||||
|
||||
test_dlsym();
|
||||
|
||||
test_memcpy();
|
||||
test_strcpy();
|
||||
|
||||
#[cfg(not(target_os = "macos"))] // reallocarray does not exist on macOS
|
||||
test_memalign();
|
||||
#[cfg(not(any(target_os = "macos", target_os = "illumos")))]
|
||||
test_reallocarray();
|
||||
|
||||
// These are Linux-specific
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
test_posix_fadvise();
|
||||
test_sync_file_range();
|
||||
test_sigrt();
|
||||
}
|
||||
test_sigrt();
|
||||
}
|
||||
|
@ -1,9 +1,29 @@
|
||||
//@ignore-target-windows: no libc
|
||||
//@ignore-target-apple: no getrandom
|
||||
|
||||
use std::ptr;
|
||||
//@revisions: isolation no_isolation
|
||||
//@[no_isolation]compile-flags: -Zmiri-disable-isolation
|
||||
|
||||
fn main() {
|
||||
test_getentropy();
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
test_getrandom();
|
||||
}
|
||||
|
||||
fn test_getentropy() {
|
||||
use libc::getentropy;
|
||||
|
||||
let mut buf1 = [0u8; 256];
|
||||
let mut buf2 = [0u8; 257];
|
||||
unsafe {
|
||||
assert_eq!(getentropy(buf1.as_mut_ptr() as *mut libc::c_void, buf1.len()), 0);
|
||||
assert_eq!(getentropy(buf2.as_mut_ptr() as *mut libc::c_void, buf2.len()), -1);
|
||||
assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::EIO);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fn test_getrandom() {
|
||||
use std::ptr;
|
||||
|
||||
let mut buf = [0u8; 5];
|
||||
unsafe {
|
||||
#[cfg(target_os = "linux")]
|
93
src/tools/miri/tests/pass-dep/shims/libc-time.rs
Normal file
93
src/tools/miri/tests/pass-dep/shims/libc-time.rs
Normal file
@ -0,0 +1,93 @@
|
||||
//@ignore-target-windows: no libc on Windows
|
||||
//@compile-flags: -Zmiri-disable-isolation
|
||||
use std::ffi::CStr;
|
||||
use std::{env, mem, ptr};
|
||||
|
||||
fn main() {
|
||||
test_clocks();
|
||||
test_posix_gettimeofday();
|
||||
test_localtime_r();
|
||||
}
|
||||
|
||||
/// Tests whether clock support exists at all
|
||||
fn test_clocks() {
|
||||
let mut tp = mem::MaybeUninit::<libc::timespec>::uninit();
|
||||
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, tp.as_mut_ptr()) };
|
||||
assert_eq!(is_error, 0);
|
||||
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, tp.as_mut_ptr()) };
|
||||
assert_eq!(is_error, 0);
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
{
|
||||
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME_COARSE, tp.as_mut_ptr()) };
|
||||
assert_eq!(is_error, 0);
|
||||
let is_error =
|
||||
unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_COARSE, tp.as_mut_ptr()) };
|
||||
assert_eq!(is_error, 0);
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_UPTIME_RAW, tp.as_mut_ptr()) };
|
||||
assert_eq!(is_error, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_posix_gettimeofday() {
|
||||
let mut tp = mem::MaybeUninit::<libc::timeval>::uninit();
|
||||
let tz = ptr::null_mut::<libc::timezone>();
|
||||
let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz.cast()) };
|
||||
assert_eq!(is_error, 0);
|
||||
let tv = unsafe { tp.assume_init() };
|
||||
assert!(tv.tv_sec > 0);
|
||||
assert!(tv.tv_usec >= 0); // Theoretically this could be 0.
|
||||
|
||||
// Test that non-null tz returns an error.
|
||||
let mut tz = mem::MaybeUninit::<libc::timezone>::uninit();
|
||||
let tz_ptr = tz.as_mut_ptr();
|
||||
let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr.cast()) };
|
||||
assert_eq!(is_error, -1);
|
||||
}
|
||||
|
||||
fn test_localtime_r() {
|
||||
// Set timezone to GMT.
|
||||
let key = "TZ";
|
||||
env::set_var(key, "GMT");
|
||||
|
||||
const TIME_SINCE_EPOCH: libc::time_t = 1712475836;
|
||||
let custom_time_ptr = &TIME_SINCE_EPOCH;
|
||||
let mut tm = libc::tm {
|
||||
tm_sec: 0,
|
||||
tm_min: 0,
|
||||
tm_hour: 0,
|
||||
tm_mday: 0,
|
||||
tm_mon: 0,
|
||||
tm_year: 0,
|
||||
tm_wday: 0,
|
||||
tm_yday: 0,
|
||||
tm_isdst: 0,
|
||||
tm_gmtoff: 0,
|
||||
tm_zone: std::ptr::null_mut::<libc::c_char>(),
|
||||
};
|
||||
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
|
||||
|
||||
assert_eq!(tm.tm_sec, 56);
|
||||
assert_eq!(tm.tm_min, 43);
|
||||
assert_eq!(tm.tm_hour, 7);
|
||||
assert_eq!(tm.tm_mday, 7);
|
||||
assert_eq!(tm.tm_mon, 3);
|
||||
assert_eq!(tm.tm_year, 124);
|
||||
assert_eq!(tm.tm_wday, 0);
|
||||
assert_eq!(tm.tm_yday, 97);
|
||||
assert_eq!(tm.tm_isdst, -1);
|
||||
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))]
|
||||
assert_eq!(tm.tm_gmtoff, 0);
|
||||
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))]
|
||||
unsafe {
|
||||
assert_eq!(CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00")
|
||||
};
|
||||
|
||||
// The returned value is the pointer passed in.
|
||||
assert!(ptr::eq(res, &mut tm));
|
||||
|
||||
// Remove timezone setting.
|
||||
env::remove_var(key);
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
//@ignore-target-windows: No libc on Windows
|
||||
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use core::ptr;
|
||||
|
||||
fn main() {
|
||||
// A normal allocation.
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 8;
|
||||
let size = 64;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
assert!(!ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
ptr.cast::<u8>().write_bytes(1, size);
|
||||
libc::free(ptr);
|
||||
}
|
||||
|
||||
// Align > size.
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 64;
|
||||
let size = 8;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
assert!(!ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
ptr.cast::<u8>().write_bytes(1, size);
|
||||
libc::free(ptr);
|
||||
}
|
||||
|
||||
// Size not multiple of align
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 16;
|
||||
let size = 31;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
assert!(!ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
ptr.cast::<u8>().write_bytes(1, size);
|
||||
libc::free(ptr);
|
||||
}
|
||||
|
||||
// Size == 0
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::null_mut();
|
||||
let align = 64;
|
||||
let size = 0;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
|
||||
// We are not required to return null if size == 0, but we currently do.
|
||||
// It's fine to remove this assert if we start returning non-null pointers.
|
||||
assert!(ptr.is_null());
|
||||
assert!(ptr.is_aligned_to(align));
|
||||
// Regardless of what we return, it must be `free`able.
|
||||
libc::free(ptr);
|
||||
}
|
||||
|
||||
// Non-power of 2 align
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::without_provenance_mut(0x1234567);
|
||||
let align = 15;
|
||||
let size = 8;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), libc::EINVAL);
|
||||
// The pointer is not modified on failure, posix_memalign(3) says:
|
||||
// > On Linux (and other systems), posix_memalign() does not modify memptr on failure.
|
||||
// > A requirement standardizing this behavior was added in POSIX.1-2008 TC2.
|
||||
assert_eq!(ptr.addr(), 0x1234567);
|
||||
}
|
||||
|
||||
// Too small align (smaller than ptr)
|
||||
unsafe {
|
||||
let mut ptr: *mut libc::c_void = ptr::without_provenance_mut(0x1234567);
|
||||
let align = std::mem::size_of::<usize>() / 2;
|
||||
let size = 8;
|
||||
assert_eq!(libc::posix_memalign(&mut ptr, align, size), libc::EINVAL);
|
||||
// The pointer is not modified on failure, posix_memalign(3) says:
|
||||
// > On Linux (and other systems), posix_memalign() does not modify memptr on failure.
|
||||
// > A requirement standardizing this behavior was added in POSIX.1-2008 TC2.
|
||||
assert_eq!(ptr.addr(), 0x1234567);
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
eprintln!("Hello, error!");
|
||||
}
|
||||
|
1
src/tools/miri/tests/pass/hello.stderr
Normal file
1
src/tools/miri/tests/pass/hello.stderr
Normal file
@ -0,0 +1 @@
|
||||
Hello, error!
|
Loading…
x
Reference in New Issue
Block a user