Skip to content

Miri subtree update #129230

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

Merged
merged 85 commits into from
Aug 18, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
2c166f4
Make unused states of Reserved unrepresentable
Vanille-N Jul 18, 2024
2431949
Preparing for merge from rustc
Aug 6, 2024
ac3e349
Merge from rustc
Aug 6, 2024
5b2cdc1
Auto merge of #3791 - rust-lang:rustup-2024-08-06, r=RalfJung
bors Aug 6, 2024
7d066c4
add return-place-protection tail-call test, and fix previous test
RalfJung Aug 6, 2024
c755314
Auto merge of #3792 - RalfJung:test-typo, r=RalfJung
bors Aug 6, 2024
0475bdd
Preparing for merge from rustc
Aug 7, 2024
630ad88
Merge from rustc
Aug 7, 2024
384f858
Auto merge of #3793 - rust-lang:rustup-2024-08-07, r=RalfJung
bors Aug 7, 2024
deeccf9
remove some SSE/SSE2 intrinsics that are no longer used by stdarch
RalfJung Jul 13, 2024
2f405eb
Auto merge of #3747 - RalfJung:sse-cleanup, r=RalfJung
bors Aug 7, 2024
d480954
allow all code to call getuid()
RalfJung Aug 7, 2024
dc9f4e8
Auto merge of #3794 - RalfJung:getuid, r=RalfJung
bors Aug 7, 2024
21f9175
Don't panic on `miri_print_borrow_state()` under `-Zmiri-disable-stac…
zachs18 Aug 8, 2024
6552a82
throw_unsup_format for alignment greater than 2^29 and refactor non-p…
tiif Aug 9, 2024
dc54f1d
miri_print_borrow_state: show error when AllocId is invalid
RalfJung Aug 9, 2024
d36e157
Auto merge of #3797 - zachs18:no-borrow-no-panic, r=RalfJung
bors Aug 9, 2024
bb04eab
Auto merge of #3795 - tiif:ice-layout-limit, r=RalfJung
bors Aug 10, 2024
f7c938a
miri-script: use --remap-path-prefix to print errors relative to the …
RalfJung Aug 9, 2024
d2e0970
update suggested RA config; the './miri cargo' command is not needed …
RalfJung Aug 9, 2024
118be41
add './miri doc' command
RalfJung Aug 9, 2024
8197f07
miri-script: pass around the relative crate dir, not the absolute pat…
RalfJung Aug 10, 2024
feab324
CI: we now need the nightly toolchain as well
RalfJung Aug 10, 2024
1156d58
josh: wait until the socket is ready
RalfJung Aug 10, 2024
3456432
Auto merge of #3799 - RalfJung:josh-wait, r=RalfJung
bors Aug 10, 2024
2b83935
josh-proxy: fix wait-for-josh logic
RalfJung Aug 12, 2024
3652011
Preparing for merge from rustc
RalfJung Aug 12, 2024
c0c6f28
Merge from rustc
RalfJung Aug 12, 2024
7383401
clippy fix
RalfJung Aug 12, 2024
14f9284
fix tree borrows Unique test
RalfJung Aug 12, 2024
a25ec22
Auto merge of #3801 - RalfJung:rustup, r=RalfJung
bors Aug 12, 2024
dd600ef
Auto merge of #3798 - RalfJung:miri-script-remap-path-prefix, r=saethlin
bors Aug 12, 2024
10cb5aa
borrow_tracker: update comments regarding protectors
RalfJung Aug 13, 2024
7db942b
remove the concept of a Call ID
RalfJung Aug 13, 2024
6ff09af
Auto merge of #3802 - RalfJung:no-more-call-id, r=RalfJung
bors Aug 13, 2024
ef91e65
Preparing for merge from rustc
RalfJung Aug 14, 2024
b65cdff
Merge from rustc
RalfJung Aug 14, 2024
c964938
fmt
RalfJung Aug 14, 2024
db3f921
CI: need nightly toolchain for auto-rustup PR
RalfJung Aug 14, 2024
f25ca7e
Auto merge of #3803 - RalfJung:rustup, r=RalfJung
bors Aug 14, 2024
607c4f5
Implement epoll shim
tiif Aug 14, 2024
86783be
Auto merge of #3712 - tiif:feat/epoll, r=oli-obk
bors Aug 14, 2024
293be46
add 'project' process guidlines for larger contributions
RalfJung Aug 15, 2024
17cfbc6
FD: remove big surrounding RefCell, simplify socketpair
RalfJung Aug 16, 2024
82c39ff
buf_has_writer is not needed any more
RalfJung Aug 16, 2024
34aec7c
make ecx.check_and_update_readiness a truly private helper function
RalfJung Aug 16, 2024
b4ab820
epoll test cleanup
RalfJung Aug 16, 2024
edd1efb
comment and test regarding notifications on writes that dont change r…
RalfJung Aug 16, 2024
9184eb5
more epoll test cleanup
RalfJung Aug 16, 2024
883e477
explain the behavior on closed peers
RalfJung Aug 16, 2024
1a51dd9
Auto merge of #3754 - Vanille-N:master, r=RalfJung
bors Aug 16, 2024
83f1b38
Auto merge of #3809 - RalfJung:fd-refcell, r=oli-obk
bors Aug 16, 2024
0951107
Add epoll EPOLLHUP flag support
tiif Aug 16, 2024
f9a7d6e
Move epoll_ready_events.epollhup = true up
tiif Aug 17, 2024
23b57e8
Preparing for merge from rustc
Aug 17, 2024
dc0faec
Merge from rustc
Aug 17, 2024
94e57fc
Auto merge of #3807 - RalfJung:projects, r=oli-obk
bors Aug 17, 2024
cab81d3
extend comments on HUP vs RDHUP
RalfJung Aug 17, 2024
78dfb8a
Auto merge of #3814 - tiif:epollhup, r=RalfJung
bors Aug 17, 2024
99d742e
implement pipe and pipe2
RalfJung Aug 16, 2024
5402be8
socketpair: test behavior when one end got closed
RalfJung Aug 17, 2024
5d59bde
test cleanup
RalfJung Aug 17, 2024
02a1166
SocketPair -> AnonSocket, because a single FD is not a pair
RalfJung Aug 17, 2024
db9a97f
Auto merge of #3817 - rust-lang:rustup-2024-08-17, r=RalfJung
bors Aug 17, 2024
465df56
epoll test: further clean up check_epoll_wait
RalfJung Aug 17, 2024
a4222b9
Auto merge of #3815 - RalfJung:pipe, r=RalfJung
bors Aug 17, 2024
0058752
Auto merge of #3819 - RalfJung:epoll-test, r=RalfJung
bors Aug 17, 2024
34e8245
tls_leak_main_thread_allowed: make test check target_thread_local
RalfJung Aug 17, 2024
bd4ef64
run TLS tests on Solarish
RalfJung Aug 17, 2024
3e698d0
Auto merge of #3822 - RalfJung:tls, r=RalfJung
bors Aug 17, 2024
7c81120
simplify synchronization object creation logic
RalfJung Aug 17, 2024
6ac5bbb
Auto merge of #3823 - RalfJung:sync, r=RalfJung
bors Aug 17, 2024
56eee8e
Apply test fix
tiif Aug 16, 2024
8821108
Auto merge of #3813 - tiif:epollfix, r=RalfJung
bors Aug 17, 2024
c0e799d
Set EINVAL for epoll_wait maxevent value 0
tiif Aug 17, 2024
8ae118d
Move the maxevents.try_into().unwrap() after value check
tiif Aug 18, 2024
f918de8
make sure we read all arguments before returning early
RalfJung Aug 18, 2024
aea3cfd
Auto merge of #3824 - tiif:maxeventeinval, r=RalfJung
bors Aug 18, 2024
e614d7d
epoll test_socketpair_read: explicitly check real and Miri behavior
RalfJung Aug 18, 2024
8f2768b
Auto merge of #3825 - RalfJung:epoll-miri, r=RalfJung
bors Aug 18, 2024
6702f15
epoll: iterate through output buffer then fetch an event from ready list
tiif Aug 17, 2024
9370020
Auto merge of #3818 - tiif:loseevents, r=RalfJung
bors Aug 18, 2024
483120d
Add EPOLLER support
tiif Aug 17, 2024
c74ffd8
Auto merge of #3820 - tiif:epoller, r=RalfJung
bors Aug 18, 2024
0708b28
fix build with bootstrap compiler
RalfJung Aug 18, 2024
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
15 changes: 13 additions & 2 deletions src/tools/miri/src/shims/unix/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,14 +288,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}

// Sockets
// Unnamed sockets and pipes
"socketpair" => {
let [domain, type_, protocol, sv] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;

let result = this.socketpair(domain, type_, protocol, sv)?;
this.write_scalar(result, dest)?;
}
"pipe" => {
let [pipefd] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.pipe2(pipefd, /*flags*/ None)?;
this.write_scalar(result, dest)?;
}
"pipe2" => {
let [pipefd, flags] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.pipe2(pipefd, Some(flags))?;
this.write_scalar(result, dest)?;
}

// Time
"gettimeofday" => {
Expand Down
5 changes: 3 additions & 2 deletions src/tools/miri/src/shims/unix/linux/epoll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,10 @@ pub struct EpollEventInterest {

/// EpollReadyEvents reflects the readiness of a file description.
pub struct EpollReadyEvents {
/// The associated file is available for read(2) operations.
/// The associated file is available for read(2) operations, in the sense that a read will not block.
/// (I.e., returning EOF is considered "ready".)
pub epollin: bool,
/// The associated file is available for write(2) operations.
/// The associated file is available for write(2) operations, in the sense that a write will not block.
pub epollout: bool,
/// Stream socket peer closed connection, or shut down writing
/// half of connection.
Expand Down
4 changes: 2 additions & 2 deletions src/tools/miri/src/shims/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ mod env;
mod fd;
mod fs;
mod mem;
mod socket;
mod sync;
mod thread;
mod unnamed_socket;

mod android;
mod freebsd;
Expand All @@ -23,9 +23,9 @@ pub use env::EvalContextExt as _;
pub use fd::EvalContextExt as _;
pub use fs::EvalContextExt as _;
pub use mem::EvalContextExt as _;
pub use socket::EvalContextExt as _;
pub use sync::EvalContextExt as _;
pub use thread::EvalContextExt as _;
pub use unnamed_socket::EvalContextExt as _;

// Make up some constants.
const UID: u32 = 1000;
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//! This implements "anonymous" sockets, that do not correspond to anything on the host system and
//! are entirely implemented inside Miri.
//! We also use the same infrastructure to implement unnamed pipes.
use std::cell::{OnceCell, RefCell};
use std::collections::VecDeque;
use std::io;
Expand All @@ -13,12 +17,13 @@ use crate::{concurrency::VClock, *};
/// be configured in the real system.
const MAX_SOCKETPAIR_BUFFER_CAPACITY: usize = 212992;

/// Pair of connected sockets.
/// One end of a pair of connected unnamed sockets.
#[derive(Debug)]
struct SocketPair {
/// The buffer we are reading from.
readbuf: RefCell<Buffer>,
/// The `SocketPair` file descriptor that is our "peer", and that holds the buffer we are
struct AnonSocket {
/// The buffer we are reading from, or `None` if this is the writing end of a pipe.
/// (In that case, the peer FD will be the reading end of that pipe.)
readbuf: Option<RefCell<Buffer>>,
/// The `AnonSocket` file descriptor that is our "peer", and that holds the buffer we are
/// writing to. This is a weak reference because the other side may be closed before us; all
/// future writes will then trigger EPIPE.
peer_fd: OnceCell<WeakFileDescriptionRef>,
Expand All @@ -37,13 +42,13 @@ impl Buffer {
}
}

impl SocketPair {
impl AnonSocket {
fn peer_fd(&self) -> &WeakFileDescriptionRef {
self.peer_fd.get().unwrap()
}
}

impl FileDescription for SocketPair {
impl FileDescription for AnonSocket {
fn name(&self) -> &'static str {
"socketpair"
}
Expand All @@ -55,17 +60,25 @@ impl FileDescription for SocketPair {
let mut epoll_ready_events = EpollReadyEvents::new();

// Check if it is readable.
let readbuf = self.readbuf.borrow();
if !readbuf.buf.is_empty() {
if let Some(readbuf) = &self.readbuf {
if !readbuf.borrow().buf.is_empty() {
epoll_ready_events.epollin = true;
}
} else {
// Without a read buffer, reading never blocks, so we are always ready.
epoll_ready_events.epollin = true;
}

// Check if is writable.
if let Some(peer_fd) = self.peer_fd().upgrade() {
let writebuf = &peer_fd.downcast::<SocketPair>().unwrap().readbuf.borrow();
let data_size = writebuf.buf.len();
let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(data_size);
if available_space != 0 {
if let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf {
let data_size = writebuf.borrow().buf.len();
let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(data_size);
if available_space != 0 {
epoll_ready_events.epollout = true;
}
} else {
// Without a write buffer, writing never blocks.
epoll_ready_events.epollout = true;
}
} else {
Expand Down Expand Up @@ -108,7 +121,12 @@ impl FileDescription for SocketPair {
return Ok(Ok(0));
}

let mut readbuf = self.readbuf.borrow_mut();
let Some(readbuf) = &self.readbuf else {
// FIXME: This should return EBADF, but there's no nice way to do that as there's no
// corresponding ErrorKind variant.
throw_unsup_format!("reading from the write end of a pipe");
};
let mut readbuf = readbuf.borrow_mut();
if readbuf.buf.is_empty() {
if self.peer_fd().upgrade().is_none() {
// Socketpair with no peer and empty buffer.
Expand Down Expand Up @@ -176,7 +194,13 @@ impl FileDescription for SocketPair {
// closed.
return Ok(Err(Error::from(ErrorKind::BrokenPipe)));
};
let mut writebuf = peer_fd.downcast::<SocketPair>().unwrap().readbuf.borrow_mut();

let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf else {
// FIXME: This should return EBADF, but there's no nice way to do that as there's no
// corresponding ErrorKind variant.
throw_unsup_format!("writing to the reading end of a pipe");
};
let mut writebuf = writebuf.borrow_mut();
let data_size = writebuf.buf.len();
let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(data_size);
if available_space == 0 {
Expand Down Expand Up @@ -227,12 +251,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {

let mut is_sock_nonblock = false;

// Parse and remove the type flags that we support. If type != 0 after removing,
// unsupported flags are used.
if type_ & this.eval_libc_i32("SOCK_STREAM") == this.eval_libc_i32("SOCK_STREAM") {
type_ &= !(this.eval_libc_i32("SOCK_STREAM"));
}

// Parse and remove the type flags that we support.
// SOCK_NONBLOCK only exists on Linux.
if this.tcx.sess.target.os == "linux" {
if type_ & this.eval_libc_i32("SOCK_NONBLOCK") == this.eval_libc_i32("SOCK_NONBLOCK") {
Expand All @@ -253,7 +272,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
and AF_LOCAL are allowed",
domain
);
} else if type_ != 0 {
} else if type_ != this.eval_libc_i32("SOCK_STREAM") {
throw_unsup_format!(
"socketpair: type {:#x} is unsupported, only SOCK_STREAM, \
SOCK_CLOEXEC and SOCK_NONBLOCK are allowed",
Expand All @@ -268,20 +287,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {

// Generate file descriptions.
let fds = &mut this.machine.fds;
let fd0 = fds.new_ref(SocketPair {
readbuf: RefCell::new(Buffer::new()),
let fd0 = fds.new_ref(AnonSocket {
readbuf: Some(RefCell::new(Buffer::new())),
peer_fd: OnceCell::new(),
is_nonblock: is_sock_nonblock,
});
let fd1 = fds.new_ref(SocketPair {
readbuf: RefCell::new(Buffer::new()),
let fd1 = fds.new_ref(AnonSocket {
readbuf: Some(RefCell::new(Buffer::new())),
peer_fd: OnceCell::new(),
is_nonblock: is_sock_nonblock,
});

// Make the file descriptions point to each other.
fd0.downcast::<SocketPair>().unwrap().peer_fd.set(fd1.downgrade()).unwrap();
fd1.downcast::<SocketPair>().unwrap().peer_fd.set(fd0.downgrade()).unwrap();
fd0.downcast::<AnonSocket>().unwrap().peer_fd.set(fd1.downgrade()).unwrap();
fd1.downcast::<AnonSocket>().unwrap().peer_fd.set(fd0.downgrade()).unwrap();

// Insert the file description to the fd table, generating the file descriptors.
let sv0 = fds.insert(fd0);
Expand All @@ -295,4 +314,51 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {

Ok(Scalar::from_i32(0))
}

fn pipe2(
&mut self,
pipefd: &OpTy<'tcx>,
flags: Option<&OpTy<'tcx>>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();

let pipefd = this.deref_pointer(pipefd)?;
let flags = match flags {
Some(flags) => this.read_scalar(flags)?.to_i32()?,
None => 0,
};

// As usual we ignore CLOEXEC.
let cloexec = this.eval_libc_i32("O_CLOEXEC");
if flags != 0 && flags != cloexec {
throw_unsup_format!("unsupported flags in `pipe2`");
}

// Generate file descriptions.
// pipefd[0] refers to the read end of the pipe.
let fds = &mut this.machine.fds;
let fd0 = fds.new_ref(AnonSocket {
readbuf: Some(RefCell::new(Buffer::new())),
peer_fd: OnceCell::new(),
is_nonblock: false,
});
let fd1 =
fds.new_ref(AnonSocket { readbuf: None, peer_fd: OnceCell::new(), is_nonblock: false });

// Make the file descriptions point to each other.
fd0.downcast::<AnonSocket>().unwrap().peer_fd.set(fd1.downgrade()).unwrap();
fd1.downcast::<AnonSocket>().unwrap().peer_fd.set(fd0.downgrade()).unwrap();

// Insert the file description to the fd table, generating the file descriptors.
let pipefd0 = fds.insert(fd0);
let pipefd1 = fds.insert(fd1);

// Return file descriptors to the caller.
let pipefd0 = Scalar::from_int(pipefd0, pipefd.layout.size);
let pipefd1 = Scalar::from_int(pipefd1, pipefd.layout.size);
this.write_scalar(pipefd0, &pipefd)?;
this.write_scalar(pipefd1, &pipefd.offset(pipefd.layout.size, pipefd.layout, this)?)?;

Ok(Scalar::from_i32(0))
}
}
99 changes: 99 additions & 0 deletions src/tools/miri/tests/pass-dep/libc/libc-pipe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//@ignore-target-windows: No libc pipe on Windows
// test_race depends on a deterministic schedule.
//@compile-flags: -Zmiri-preemption-rate=0
use std::thread;
fn main() {
test_pipe();
test_pipe_threaded();
test_race();
}

fn test_pipe() {
let mut fds = [-1, -1];
let res = unsafe { libc::pipe(fds.as_mut_ptr()) };
assert_eq!(res, 0);

// Read size == data available in buffer.
let data = "12345".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
let mut buf3: [u8; 5] = [0; 5];
let res = unsafe { libc::read(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t) };
assert_eq!(res, 5);
assert_eq!(buf3, "12345".as_bytes());

// Read size > data available in buffer.
let data = "123".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 3) };
assert_eq!(res, 3);
let mut buf4: [u8; 5] = [0; 5];
let res = unsafe { libc::read(fds[0], buf4.as_mut_ptr().cast(), buf4.len() as libc::size_t) };
assert_eq!(res, 3);
assert_eq!(&buf4[0..3], "123".as_bytes());
}

fn test_pipe_threaded() {
let mut fds = [-1, -1];
let res = unsafe { libc::pipe(fds.as_mut_ptr()) };
assert_eq!(res, 0);

let thread1 = thread::spawn(move || {
let mut buf: [u8; 5] = [0; 5];
let res: i64 = unsafe {
libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
.try_into()
.unwrap()
};
assert_eq!(res, 5);
assert_eq!(buf, "abcde".as_bytes());
});
// FIXME: we should yield here once blocking is implemented.
//thread::yield_now();
let data = "abcde".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
thread1.join().unwrap();

// Read and write from different direction
let thread2 = thread::spawn(move || {
// FIXME: we should yield here once blocking is implemented.
//thread::yield_now();
let data = "12345".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
assert_eq!(res, 5);
});
// FIXME: we should not yield here once blocking is implemented.
thread::yield_now();
let mut buf: [u8; 5] = [0; 5];
let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
assert_eq!(res, 5);
assert_eq!(buf, "12345".as_bytes());
thread2.join().unwrap();
}

fn test_race() {
static mut VAL: u8 = 0;
let mut fds = [-1, -1];
let res = unsafe { libc::pipe(fds.as_mut_ptr()) };
assert_eq!(res, 0);
let thread1 = thread::spawn(move || {
let mut buf: [u8; 1] = [0; 1];
// write() from the main thread will occur before the read() here
// because preemption is disabled and the main thread yields after write().
let res: i32 = unsafe {
libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t)
.try_into()
.unwrap()
};
assert_eq!(res, 1);
assert_eq!(buf, "a".as_bytes());
// The read above establishes a happens-before so it is now safe to access this global variable.
unsafe { assert_eq!(VAL, 1) };
});
unsafe { VAL = 1 };
let data = "a".as_bytes().as_ptr();
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 1) };
assert_eq!(res, 1);
thread::yield_now();
thread1.join().unwrap();
}
Loading