From fb9b6bd5351c3eb4f62da83c35ca1636a3523a60 Mon Sep 17 00:00:00 2001 From: hulxv Date: Wed, 17 Dec 2025 22:57:32 +0200 Subject: [PATCH 1/2] Refactor socketpair tests to use utility functions for reading and writing inline byte slices for data writes Refactor socketpair tests to use utility functions for reading and writing --- .../tests/pass-dep/libc/libc-socketpair.rs | 189 ++++++------------ 1 file changed, 61 insertions(+), 128 deletions(-) diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs index ce3927ce48ca..2031149aaf4f 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs @@ -9,6 +9,7 @@ use std::thread; #[path = "../../utils/libc.rs"] mod libc_utils; +use libc_utils::*; fn main() { test_socketpair(); @@ -21,139 +22,92 @@ fn main() { fn test_socketpair() { let mut fds = [-1, -1]; - let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); // Read size == data available in buffer. - let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; - assert_eq!(res, 5); - let mut buf: [u8; 5] = [0; 5]; - let res = - unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - assert_eq!(res, 5); - assert_eq!(buf, "abcde".as_bytes()); + let data = b"abcde"; + write_all_from_slice(fds[0], data).unwrap(); + let buf = read_all_into_array::<5>(fds[1]).unwrap(); + assert_eq!(&buf, data); // Read size > data available in buffer. - let data = "abc".as_bytes(); - let res = unsafe { libc_utils::write_all(fds[0], data.as_ptr() as *const libc::c_void, 3) }; - assert_eq!(res, 3); + let data = b"abc"; + write_all_from_slice(fds[0], data).unwrap(); let mut buf2: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[1], buf2.as_mut_ptr().cast(), buf2.len() as libc::size_t) }; - assert!(res > 0 && res <= 3); - let res = res as usize; - assert_eq!(buf2[..res], data[..res]); - if res < 3 { - // Drain the rest from the read end. - let res = unsafe { libc_utils::read_all(fds[1], buf2[res..].as_mut_ptr().cast(), 3 - res) }; - assert!(res > 0); - } + let (read, rest) = read_into_slice(fds[1], &mut buf2).unwrap(); + assert_eq!(read[..], data[..read.len()]); + // Write 2 more bytes so we can exactly fill the `rest`. + write_all_from_slice(fds[0], b"12").unwrap(); + read_all_into_slice(fds[1], rest).unwrap(); // Test read and write from another direction. // Read size == data available in buffer. - let data = "12345".as_bytes().as_ptr(); - let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; - assert_eq!(res, 5); - let mut buf3: [u8; 5] = [0; 5]; - let res = unsafe { - libc_utils::read_all(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t) - }; - assert_eq!(res, 5); - assert_eq!(buf3, "12345".as_bytes()); + let data = b"12345"; + write_all_from_slice(fds[1], data).unwrap(); + let buf3 = read_all_into_array::<5>(fds[0]).unwrap(); + assert_eq!(&buf3, data); // Read size > data available in buffer. - let data = "123".as_bytes(); - let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 3) }; - assert_eq!(res, 3); + let data = b"123"; + write_all_from_slice(fds[1], data).unwrap(); 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!(res > 0 && res <= 3); - let res = res as usize; - assert_eq!(buf4[..res], data[..res]); - if res < 3 { - // Drain the rest from the read end. - let res = unsafe { libc_utils::read_all(fds[0], buf4[res..].as_mut_ptr().cast(), 3 - res) }; - assert!(res > 0); - } + let (read, rest) = read_into_slice(fds[0], &mut buf4).unwrap(); + assert_eq!(read[..], data[..read.len()]); + // Write 2 more bytes so we can exactly fill the `rest`. + write_all_from_slice(fds[1], b"12").unwrap(); + read_all_into_slice(fds[0], rest).unwrap(); // Test when happens when we close one end, with some data in the buffer. - let res = unsafe { libc_utils::write_all(fds[0], data.as_ptr() as *const libc::c_void, 3) }; - assert_eq!(res, 3); - unsafe { libc::close(fds[0]) }; + write_all_from_slice(fds[0], data).unwrap(); + errno_check(unsafe { libc::close(fds[0]) }); // Reading the other end should return that data, then EOF. let mut buf: [u8; 5] = [0; 5]; - let res = - unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - assert_eq!(res, 3); - assert_eq!(&buf[0..3], "123".as_bytes()); - let res = - unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + read_all_into_slice(fds[1], &mut buf[0..3]).unwrap(); + assert_eq!(&buf[0..3], data); + let res = read_into_slice(fds[1], &mut buf[3..5]).unwrap().0.len(); assert_eq!(res, 0); // 0-sized read: EOF. // Writing the other end should emit EPIPE. - let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 1) }; - assert_eq!(res, -1); + let res = write_all_from_slice(fds[1], &mut buf); + assert_eq!(res, Err(-1)); assert_eq!(std::io::Error::last_os_error().raw_os_error(), Some(libc::EPIPE)); } fn test_socketpair_threaded() { let mut fds = [-1, -1]; - let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); let thread1 = thread::spawn(move || { - let mut buf: [u8; 5] = [0; 5]; - let res: i64 = unsafe { - libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) - .try_into() - .unwrap() - }; - assert_eq!(res, 5); - assert_eq!(buf, "abcde".as_bytes()); + let buf = read_all_into_array::<5>(fds[1]).unwrap(); + assert_eq!(&buf, b"abcde"); }); thread::yield_now(); - let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; - assert_eq!(res, 5); + write_all_from_slice(fds[0], b"abcde").unwrap(); thread1.join().unwrap(); // Read and write from different direction let thread2 = thread::spawn(move || { thread::yield_now(); - let data = "12345".as_bytes().as_ptr(); - let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; - assert_eq!(res, 5); + write_all_from_slice(fds[1], b"12345").unwrap(); }); - let mut buf: [u8; 5] = [0; 5]; - let res = - unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - assert_eq!(res, 5); - assert_eq!(buf, "12345".as_bytes()); + let buf = read_all_into_array::<5>(fds[0]).unwrap(); + assert_eq!(&buf, b"12345"); thread2.join().unwrap(); } fn test_race() { static mut VAL: u8 = 0; let mut fds = [-1, -1]; - let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); 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_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) - .try_into() - .unwrap() - }; - assert_eq!(res, 1); - assert_eq!(buf, "a".as_bytes()); + let buf = read_all_into_array::<1>(fds[1]).unwrap(); + assert_eq!(&buf, b"a"); // 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_utils::write_all(fds[0], data as *const libc::c_void, 1) }; - assert_eq!(res, 1); + write_all_from_slice(fds[0], b"a").unwrap(); thread::yield_now(); thread1.join().unwrap(); } @@ -161,22 +115,15 @@ fn test_race() { // Test the behaviour of a socketpair getting blocked on read and subsequently unblocked. fn test_blocking_read() { let mut fds = [-1, -1]; - let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); let thread1 = thread::spawn(move || { // Let this thread block on read. - let mut buf: [u8; 3] = [0; 3]; - let res = unsafe { - libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) - }; - assert_eq!(res, 3); - assert_eq!(&buf, "abc".as_bytes()); + let buf = read_all_into_array::<3>(fds[1]).unwrap(); + assert_eq!(&buf, b"abc"); }); let thread2 = thread::spawn(move || { // Unblock thread1 by doing writing something. - let data = "abc".as_bytes().as_ptr(); - let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 3) }; - assert_eq!(res, 3); + write_all_from_slice(fds[0], b"abc").unwrap(); }); thread1.join().unwrap(); thread2.join().unwrap(); @@ -185,26 +132,17 @@ fn test_blocking_read() { // Test the behaviour of a socketpair getting blocked on write and subsequently unblocked. fn test_blocking_write() { let mut fds = [-1, -1]; - let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); let arr1: [u8; 0x34000] = [1; 0x34000]; // Exhaust the space in the buffer so the subsequent write will block. - let res = - unsafe { libc_utils::write_all(fds[0], arr1.as_ptr() as *const libc::c_void, arr1.len()) }; - assert_eq!(res, 0x34000); + write_all_from_slice(fds[0], &arr1).unwrap(); let thread1 = thread::spawn(move || { - let data = "abc".as_bytes().as_ptr(); // The write below will be blocked because the buffer is already full. - let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 3) }; - assert_eq!(res, 3); + write_all_from_slice(fds[0], b"abc").unwrap(); }); let thread2 = thread::spawn(move || { // Unblock thread1 by freeing up some space. - let mut buf: [u8; 3] = [0; 3]; - let res = unsafe { - libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) - }; - assert_eq!(res, 3); + let buf = read_all_into_array::<3>(fds[1]).unwrap(); assert_eq!(buf, [1, 1, 1]); }); thread1.join().unwrap(); @@ -215,30 +153,25 @@ fn test_blocking_write() { fn test_socketpair_setfl_getfl() { // Initialise socketpair fds. let mut fds = [-1, -1]; - let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); // Test if both sides have O_RDWR. - let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) }; - assert_eq!(res, libc::O_RDWR); - let res = unsafe { libc::fcntl(fds[1], libc::F_GETFL) }; - assert_eq!(res, libc::O_RDWR); + assert_eq!(errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(), libc::O_RDWR); + assert_eq!(errno_result(unsafe { libc::fcntl(fds[1], libc::F_GETFL) }).unwrap(), libc::O_RDWR); // Add the O_NONBLOCK flag with F_SETFL. - let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) }); // Test if the O_NONBLOCK flag is successfully added. - let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) }; - assert_eq!(res, libc::O_RDWR | libc::O_NONBLOCK); + assert_eq!( + errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(), + libc::O_RDWR | libc::O_NONBLOCK + ); // The other side remains unchanged. - let res = unsafe { libc::fcntl(fds[1], libc::F_GETFL) }; - assert_eq!(res, libc::O_RDWR); + assert_eq!(errno_result(unsafe { libc::fcntl(fds[1], libc::F_GETFL) }).unwrap(), libc::O_RDWR); // Test if O_NONBLOCK flag can be unset. - let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, 0) }; - assert_eq!(res, 0); - let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) }; - assert_eq!(res, libc::O_RDWR); + errno_check(unsafe { libc::fcntl(fds[0], libc::F_SETFL, 0) }); + assert_eq!(errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(), libc::O_RDWR); } From bd31b9d3bcd430a4236162d6b8e87661b8904964 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 10 Jan 2026 14:22:48 +0100 Subject: [PATCH 2/2] use io::Result for read/write helpers, and add read_until_eof_into_slice --- .../tests/pass-dep/libc/libc-socketpair.rs | 11 ++--- src/tools/miri/tests/utils/libc.rs | 46 +++++++++---------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs index 2031149aaf4f..20424fc86dc2 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs @@ -62,14 +62,11 @@ fn test_socketpair() { errno_check(unsafe { libc::close(fds[0]) }); // Reading the other end should return that data, then EOF. let mut buf: [u8; 5] = [0; 5]; - read_all_into_slice(fds[1], &mut buf[0..3]).unwrap(); - assert_eq!(&buf[0..3], data); - let res = read_into_slice(fds[1], &mut buf[3..5]).unwrap().0.len(); - assert_eq!(res, 0); // 0-sized read: EOF. + let (res, _) = read_until_eof_into_slice(fds[1], &mut buf).unwrap(); + assert_eq!(res, data); // Writing the other end should emit EPIPE. - let res = write_all_from_slice(fds[1], &mut buf); - assert_eq!(res, Err(-1)); - assert_eq!(std::io::Error::last_os_error().raw_os_error(), Some(libc::EPIPE)); + let err = write_all_from_slice(fds[1], &mut buf).unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::EPIPE)); } fn test_socketpair_threaded() { diff --git a/src/tools/miri/tests/utils/libc.rs b/src/tools/miri/tests/utils/libc.rs index 0765bacb6bd8..4e9a94718c38 100644 --- a/src/tools/miri/tests/utils/libc.rs +++ b/src/tools/miri/tests/utils/libc.rs @@ -40,21 +40,17 @@ pub unsafe fn read_all( return read_so_far as libc::ssize_t; } -/// Try to fill the given slice by reading from `fd`. Error if that many bytes could not be read. +/// Try to fill the given slice by reading from `fd`. Panic if that many bytes could not be read. #[track_caller] -pub fn read_all_into_slice(fd: libc::c_int, buf: &mut [u8]) -> Result<(), libc::ssize_t> { - let res = unsafe { read_all(fd, buf.as_mut_ptr().cast(), buf.len()) }; - if res >= 0 { - assert_eq!(res as usize, buf.len()); - Ok(()) - } else { - Err(res) - } +pub fn read_all_into_slice(fd: libc::c_int, buf: &mut [u8]) -> io::Result<()> { + let res = errno_result(unsafe { read_all(fd, buf.as_mut_ptr().cast(), buf.len()) })?; + assert_eq!(res as usize, buf.len()); + Ok(()) } /// Read exactly `N` bytes from `fd`. Error if that many bytes could not be read. #[track_caller] -pub fn read_all_into_array(fd: libc::c_int) -> Result<[u8; N], libc::ssize_t> { +pub fn read_all_into_array(fd: libc::c_int) -> io::Result<[u8; N]> { let mut buf = [0; N]; read_all_into_slice(fd, &mut buf)?; Ok(buf) @@ -63,12 +59,20 @@ pub fn read_all_into_array(fd: libc::c_int) -> Result<[u8; N], l /// Do a single read from `fd` and return the part of the buffer that was written into, /// and the rest. #[track_caller] -pub fn read_into_slice( +pub fn read_into_slice(fd: libc::c_int, buf: &mut [u8]) -> io::Result<(&mut [u8], &mut [u8])> { + let res = errno_result(unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) })?; + Ok(buf.split_at_mut(res as usize)) +} + +/// Read from `fd` until we get EOF and return the part of the buffer that was written into, +/// and the rest. +#[track_caller] +pub fn read_until_eof_into_slice( fd: libc::c_int, buf: &mut [u8], -) -> Result<(&mut [u8], &mut [u8]), libc::ssize_t> { - let res = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) }; - if res >= 0 { Ok(buf.split_at_mut(res as usize)) } else { Err(res) } +) -> io::Result<(&mut [u8], &mut [u8])> { + let res = errno_result(unsafe { read_all(fd, buf.as_mut_ptr().cast(), buf.len()) })?; + Ok(buf.split_at_mut(res as usize)) } pub unsafe fn write_all( @@ -89,16 +93,12 @@ pub unsafe fn write_all( return written_so_far as libc::ssize_t; } -/// Write the entire `buf` to `fd`. Error if not all bytes could be written. +/// Write the entire `buf` to `fd`. Panic if not all bytes could be written. #[track_caller] -pub fn write_all_from_slice(fd: libc::c_int, buf: &[u8]) -> Result<(), libc::ssize_t> { - let res = unsafe { write_all(fd, buf.as_ptr().cast(), buf.len()) }; - if res >= 0 { - assert_eq!(res as usize, buf.len()); - Ok(()) - } else { - Err(res) - } +pub fn write_all_from_slice(fd: libc::c_int, buf: &[u8]) -> io::Result<()> { + let res = errno_result(unsafe { write_all(fd, buf.as_ptr().cast(), buf.len()) })?; + assert_eq!(res as usize, buf.len()); + Ok(()) } #[cfg(any(target_os = "linux", target_os = "android", target_os = "illumos"))]