Avoid crashing under Miri

See https://github.com/rust-lang/rust/pull/124210.
reviewable/pr942/r1
Tamir Duberstein 5 months ago
parent 7e1666fb83
commit 7a7d16885a

@ -88,8 +88,80 @@ pub use programs::loaded_programs;
mod sys; mod sys;
pub mod util; pub mod util;
use std::os::fd::{AsFd, BorrowedFd, OwnedFd};
pub use bpf::*; pub use bpf::*;
pub use obj::btf::{Btf, BtfError}; pub use obj::btf::{Btf, BtfError};
pub use object::Endianness; pub use object::Endianness;
#[doc(hidden)] #[doc(hidden)]
pub use sys::netlink_set_link_up; pub use sys::netlink_set_link_up;
// See https://github.com/rust-lang/rust/pull/124210; this structure exists to avoid crashing the
// process when we try to close a fake file descriptor in Miri.
#[derive(Debug)]
struct MiriSafeFd {
#[cfg(not(miri))]
fd: OwnedFd,
#[cfg(miri)]
fd: Option<OwnedFd>,
}
impl MiriSafeFd {
#[cfg(any(test, miri))]
const MOCK_FD: u16 = 1337;
#[cfg(not(miri))]
fn from_fd(fd: OwnedFd) -> Self {
Self { fd }
}
#[cfg(miri)]
fn from_fd(fd: OwnedFd) -> Self {
Self { fd: Some(fd) }
}
#[cfg(not(miri))]
fn try_clone(&self) -> std::io::Result<Self> {
let Self { fd } = self;
let fd = fd.try_clone()?;
Ok(Self { fd })
}
#[cfg(miri)]
fn try_clone(&self) -> std::io::Result<Self> {
let Self { fd } = self;
let fd = fd.as_ref().map(OwnedFd::try_clone).transpose()?;
Ok(Self { fd })
}
}
impl AsFd for MiriSafeFd {
#[cfg(not(miri))]
fn as_fd(&self) -> BorrowedFd<'_> {
let Self { fd } = self;
fd.as_fd()
}
#[cfg(miri)]
fn as_fd(&self) -> BorrowedFd<'_> {
let Self { fd } = self;
fd.as_ref().unwrap().as_fd()
}
}
impl Drop for MiriSafeFd {
#[cfg(not(miri))]
fn drop(&mut self) {
// Intentional no-op.
}
#[cfg(miri)]
fn drop(&mut self) {
use std::os::fd::AsRawFd as _;
let Self { fd } = self;
let fd = fd.take().unwrap();
assert_eq!(fd.as_raw_fd(), Self::MOCK_FD.into());
std::mem::forget(fd)
}
}

@ -210,11 +210,26 @@ impl From<InvalidMapTypeError> for MapError {
/// A map file descriptor. /// A map file descriptor.
#[derive(Debug)] #[derive(Debug)]
pub struct MapFd(OwnedFd); pub struct MapFd {
fd: crate::MiriSafeFd,
}
impl MapFd {
fn from_fd(fd: OwnedFd) -> Self {
let fd = crate::MiriSafeFd::from_fd(fd);
Self { fd }
}
fn try_clone(&self) -> io::Result<Self> {
let Self { fd } = self;
let fd = fd.try_clone()?;
Ok(Self { fd })
}
}
impl AsFd for MapFd { impl AsFd for MapFd {
fn as_fd(&self) -> BorrowedFd<'_> { fn as_fd(&self) -> BorrowedFd<'_> {
let Self(fd) = self; let Self { fd } = self;
fd.as_fd() fd.as_fd()
} }
} }
@ -558,8 +573,10 @@ impl MapData {
io_error, io_error,
} }
})?; })?;
let fd = MapFd(fd); Ok(Self {
Ok(Self { obj, fd }) obj,
fd: MapFd::from_fd(fd),
})
} }
pub(crate) fn create_pinned_by_name<P: AsRef<Path>>( pub(crate) fn create_pinned_by_name<P: AsRef<Path>>(
@ -585,10 +602,10 @@ impl MapData {
call: "BPF_OBJ_GET", call: "BPF_OBJ_GET",
io_error, io_error,
}) { }) {
Ok(fd) => { Ok(fd) => Ok(Self {
let fd = MapFd(fd); obj,
Ok(Self { obj, fd }) fd: MapFd::from_fd(fd),
} }),
Err(_) => { Err(_) => {
let map = Self::create(obj, name, btf_fd)?; let map = Self::create(obj, name, btf_fd)?;
map.pin(&path).map_err(|error| MapError::PinError { map.pin(&path).map_err(|error| MapError::PinError {
@ -658,7 +675,7 @@ impl MapData {
let MapInfo(info) = MapInfo::new_from_fd(fd.as_fd())?; let MapInfo(info) = MapInfo::new_from_fd(fd.as_fd())?;
Ok(Self { Ok(Self {
obj: parse_map_info(info, PinningType::None), obj: parse_map_info(info, PinningType::None),
fd: MapFd(fd), fd: MapFd::from_fd(fd),
}) })
} }
@ -972,7 +989,7 @@ impl MapInfo {
pub fn fd(&self) -> Result<MapFd, MapError> { pub fn fd(&self) -> Result<MapFd, MapError> {
let Self(info) = self; let Self(info) = self;
let fd = bpf_map_get_fd_by_id(info.id)?; let fd = bpf_map_get_fd_by_id(info.id)?;
Ok(MapFd(fd)) Ok(MapFd::from_fd(fd))
} }
/// Loads a map from a pinned path in bpffs. /// Loads a map from a pinned path in bpffs.
@ -1035,7 +1052,7 @@ mod test_utils {
Syscall::Ebpf { Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_CREATE, cmd: bpf_cmd::BPF_MAP_CREATE,
.. ..
} => Ok(1337), } => Ok(crate::MiriSafeFd::MOCK_FD.into()),
call => panic!("unexpected syscall {:?}", call), call => panic!("unexpected syscall {:?}", call),
}); });
MapData::create(obj, "foo", None).unwrap() MapData::create(obj, "foo", None).unwrap()
@ -1086,13 +1103,16 @@ mod tests {
unsafe { attr.__bindgen_anon_6.__bindgen_anon_1.map_id }, unsafe { attr.__bindgen_anon_6.__bindgen_anon_1.map_id },
1234 1234
); );
Ok(42) Ok(crate::MiriSafeFd::MOCK_FD.into())
} }
Syscall::Ebpf { Syscall::Ebpf {
cmd: bpf_cmd::BPF_OBJ_GET_INFO_BY_FD, cmd: bpf_cmd::BPF_OBJ_GET_INFO_BY_FD,
attr, attr,
} => { } => {
assert_eq!(unsafe { attr.info.bpf_fd }, 42); assert_eq!(
unsafe { attr.info.bpf_fd },
crate::MiriSafeFd::MOCK_FD.into()
);
Ok(0) Ok(0)
} }
_ => Err((-1, io::Error::from_raw_os_error(EFAULT))), _ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
@ -1103,7 +1123,7 @@ mod tests {
Ok(MapData { Ok(MapData {
obj: _, obj: _,
fd, fd,
}) => assert_eq!(fd.as_fd().as_raw_fd(), 42) }) => assert_eq!(fd.as_fd().as_raw_fd(), crate::MiriSafeFd::MOCK_FD.into())
); );
} }
@ -1113,7 +1133,7 @@ mod tests {
Syscall::Ebpf { Syscall::Ebpf {
cmd: bpf_cmd::BPF_MAP_CREATE, cmd: bpf_cmd::BPF_MAP_CREATE,
.. ..
} => Ok(42), } => Ok(crate::MiriSafeFd::MOCK_FD.into()),
_ => Err((-1, io::Error::from_raw_os_error(EFAULT))), _ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
}); });
@ -1122,7 +1142,7 @@ mod tests {
Ok(MapData { Ok(MapData {
obj: _, obj: _,
fd, fd,
}) => assert_eq!(fd.as_fd().as_raw_fd(), 42) }) => assert_eq!(fd.as_fd().as_raw_fd(), crate::MiriSafeFd::MOCK_FD.into())
); );
} }

@ -1,7 +1,7 @@
use std::{ use std::{
ffi::c_void, ffi::c_void,
io, mem, io, mem,
os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}, os::fd::{AsFd, BorrowedFd},
ptr, slice, ptr, slice,
sync::atomic::{self, AtomicPtr, Ordering}, sync::atomic::{self, AtomicPtr, Ordering},
}; };
@ -88,7 +88,7 @@ pub(crate) struct PerfBuffer {
buf: AtomicPtr<perf_event_mmap_page>, buf: AtomicPtr<perf_event_mmap_page>,
size: usize, size: usize,
page_size: usize, page_size: usize,
fd: OwnedFd, fd: crate::MiriSafeFd,
} }
impl PerfBuffer { impl PerfBuffer {
@ -120,11 +120,12 @@ impl PerfBuffer {
}); });
} }
let fd = crate::MiriSafeFd::from_fd(fd);
let perf_buf = Self { let perf_buf = Self {
buf: AtomicPtr::new(buf as *mut perf_event_mmap_page), buf: AtomicPtr::new(buf as *mut perf_event_mmap_page),
fd,
size, size,
page_size, page_size,
fd,
}; };
perf_event_ioctl(perf_buf.fd.as_fd(), PERF_EVENT_IOC_ENABLE, 0) perf_event_ioctl(perf_buf.fd.as_fd(), PERF_EVENT_IOC_ENABLE, 0)
@ -259,12 +260,6 @@ impl PerfBuffer {
} }
} }
impl AsRawFd for PerfBuffer {
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}
impl AsFd for PerfBuffer { impl AsFd for PerfBuffer {
fn as_fd(&self) -> BorrowedFd<'_> { fn as_fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd() self.fd.as_fd()
@ -307,7 +302,9 @@ mod tests {
fn fake_mmap(buf: &MMappedBuf) { fn fake_mmap(buf: &MMappedBuf) {
override_syscall(|call| match call { override_syscall(|call| match call {
Syscall::PerfEventOpen { .. } | Syscall::PerfEventIoctl { .. } => Ok(42), Syscall::PerfEventOpen { .. } | Syscall::PerfEventIoctl { .. } => {
Ok(crate::MiriSafeFd::MOCK_FD.into())
}
call => panic!("unexpected syscall: {:?}", call), call => panic!("unexpected syscall: {:?}", call),
}); });
TEST_MMAP_RET.with(|ret| *ret.borrow_mut() = buf as *const _ as *mut _); TEST_MMAP_RET.with(|ret| *ret.borrow_mut() = buf as *const _ as *mut _);

@ -64,7 +64,7 @@ impl<T: BorrowMut<MapData>> AsFd for PerfEventArrayBuffer<T> {
impl<T: BorrowMut<MapData>> AsRawFd for PerfEventArrayBuffer<T> { impl<T: BorrowMut<MapData>> AsRawFd for PerfEventArrayBuffer<T> {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.buf.as_raw_fd() self.buf.as_fd().as_raw_fd()
} }
} }
@ -199,7 +199,7 @@ impl<T: BorrowMut<MapData>> PerfEventArray<T> {
let map_data: &MapData = self.map.deref().borrow(); let map_data: &MapData = self.map.deref().borrow();
let map_fd = map_data.fd().as_fd(); let map_fd = map_data.fd().as_fd();
let buf = PerfBuffer::open(index, self.page_size, page_count.unwrap_or(2))?; let buf = PerfBuffer::open(index, self.page_size, page_count.unwrap_or(2))?;
bpf_map_update_elem(map_fd, Some(&index), &buf.as_raw_fd(), 0) bpf_map_update_elem(map_fd, Some(&index), &buf.as_fd().as_raw_fd(), 0)
.map_err(|(_, io_error)| io_error)?; .map_err(|(_, io_error)| io_error)?;
Ok(PerfEventArrayBuffer { Ok(PerfEventArrayBuffer {

@ -18,9 +18,7 @@ impl SockMapFd {
/// Creates a new instance that shares the same underlying file description as [`self`]. /// Creates a new instance that shares the same underlying file description as [`self`].
pub fn try_clone(&self) -> io::Result<Self> { pub fn try_clone(&self) -> io::Result<Self> {
let Self(inner) = self; let Self(inner) = self;
let super::MapFd(inner) = inner;
let inner = inner.try_clone()?; let inner = inner.try_clone()?;
let inner = super::MapFd(inner);
Ok(Self(inner)) Ok(Self(inner))
} }
} }

@ -730,6 +730,7 @@ pub(crate) fn is_perf_link_supported() -> bool {
u.prog_type = bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT as u32; u.prog_type = bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT as u32;
if let Ok(fd) = bpf_prog_load(&mut attr) { if let Ok(fd) = bpf_prog_load(&mut attr) {
let fd = crate::MiriSafeFd::from_fd(fd);
let fd = fd.as_fd(); let fd = fd.as_fd();
matches!( matches!(
// Uses an invalid target FD so we get EBADF if supported. // Uses an invalid target FD so we get EBADF if supported.
@ -828,7 +829,9 @@ pub(crate) fn is_prog_id_supported(map_type: bpf_map_type) -> bool {
u.map_flags = 0; u.map_flags = 0;
// SAFETY: BPF_MAP_CREATE returns a new file descriptor. // SAFETY: BPF_MAP_CREATE returns a new file descriptor.
unsafe { fd_sys_bpf(bpf_cmd::BPF_MAP_CREATE, &mut attr) }.is_ok() let fd = unsafe { fd_sys_bpf(bpf_cmd::BPF_MAP_CREATE, &mut attr) };
let fd = fd.map(crate::MiriSafeFd::from_fd);
fd.is_ok()
} }
pub(crate) fn is_btf_supported() -> bool { pub(crate) fn is_btf_supported() -> bool {
@ -1100,7 +1103,7 @@ mod tests {
cmd: bpf_cmd::BPF_LINK_CREATE, cmd: bpf_cmd::BPF_LINK_CREATE,
.. ..
} => Err((-1, io::Error::from_raw_os_error(EBADF))), } => Err((-1, io::Error::from_raw_os_error(EBADF))),
_ => Ok(42), _ => Ok(crate::MiriSafeFd::MOCK_FD.into()),
}); });
let supported = is_perf_link_supported(); let supported = is_perf_link_supported();
assert!(supported); assert!(supported);
@ -1110,7 +1113,7 @@ mod tests {
cmd: bpf_cmd::BPF_LINK_CREATE, cmd: bpf_cmd::BPF_LINK_CREATE,
.. ..
} => Err((-1, io::Error::from_raw_os_error(EINVAL))), } => Err((-1, io::Error::from_raw_os_error(EINVAL))),
_ => Ok(42), _ => Ok(crate::MiriSafeFd::MOCK_FD.into()),
}); });
let supported = is_perf_link_supported(); let supported = is_perf_link_supported();
assert!(!supported); assert!(!supported);
@ -1118,7 +1121,7 @@ mod tests {
#[test] #[test]
fn test_prog_id_supported() { fn test_prog_id_supported() {
override_syscall(|_call| Ok(42)); override_syscall(|_call| Ok(crate::MiriSafeFd::MOCK_FD.into()));
// Ensure that the three map types we can check are accepted // Ensure that the three map types we can check are accepted
let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_CPUMAP); let supported = is_prog_id_supported(bpf_map_type::BPF_MAP_TYPE_CPUMAP);

@ -1758,7 +1758,7 @@ impl<T> core::borrow::BorrowMut<T> for aya::maps::MapData where T: core::marker:
pub fn aya::maps::MapData::borrow_mut(&mut self) -> &mut T pub fn aya::maps::MapData::borrow_mut(&mut self) -> &mut T
impl<T> core::convert::From<T> for aya::maps::MapData impl<T> core::convert::From<T> for aya::maps::MapData
pub fn aya::maps::MapData::from(t: T) -> T pub fn aya::maps::MapData::from(t: T) -> T
pub struct aya::maps::MapFd(_) pub struct aya::maps::MapFd
impl core::fmt::Debug for aya::maps::MapFd impl core::fmt::Debug for aya::maps::MapFd
pub fn aya::maps::MapFd::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result pub fn aya::maps::MapFd::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl std::os::fd::owned::AsFd for aya::maps::MapFd impl std::os::fd::owned::AsFd for aya::maps::MapFd

Loading…
Cancel
Save