blob: e7e7dafe18afa4df70e55e4158008e2a2624bd1d [file] [log] [blame]
#![feature(ptr_metadata)]
#![feature(portable_simd)]
#![warn(clippy::volatile_composites)]
use std::ptr::null_mut;
#[repr(C)]
#[derive(Copy, Clone, Default)]
struct MyDevRegisters {
baseaddr: usize,
count: usize,
}
#[repr(transparent)]
struct Wrapper<T>((), T, ());
// Not to be confused with std::ptr::NonNull
struct NonNull<T>(T);
impl<T> NonNull<T> {
fn write_volatile(&self, _arg: &T) {
unimplemented!("Something entirely unrelated to std::ptr::NonNull");
}
}
fn main() {
let regs = MyDevRegisters {
baseaddr: 0xabc123,
count: 42,
};
const DEVICE_ADDR: *mut MyDevRegisters = 0xdead as *mut _;
// Raw pointer methods
unsafe {
(&raw mut (*DEVICE_ADDR).baseaddr).write_volatile(regs.baseaddr); // OK
(&raw mut (*DEVICE_ADDR).count).write_volatile(regs.count); // OK
DEVICE_ADDR.write_volatile(regs);
//~^ volatile_composites
let _regs = MyDevRegisters {
baseaddr: (&raw const (*DEVICE_ADDR).baseaddr).read_volatile(), // OK
count: (&raw const (*DEVICE_ADDR).count).read_volatile(), // OK
};
let _regs = DEVICE_ADDR.read_volatile();
//~^ volatile_composites
}
// std::ptr functions
unsafe {
std::ptr::write_volatile(&raw mut (*DEVICE_ADDR).baseaddr, regs.baseaddr); // OK
std::ptr::write_volatile(&raw mut (*DEVICE_ADDR).count, regs.count); // OK
std::ptr::write_volatile(DEVICE_ADDR, regs);
//~^ volatile_composites
let _regs = MyDevRegisters {
baseaddr: std::ptr::read_volatile(&raw const (*DEVICE_ADDR).baseaddr), // OK
count: std::ptr::read_volatile(&raw const (*DEVICE_ADDR).count), // OK
};
let _regs = std::ptr::read_volatile(DEVICE_ADDR);
//~^ volatile_composites
}
// core::ptr functions
unsafe {
core::ptr::write_volatile(&raw mut (*DEVICE_ADDR).baseaddr, regs.baseaddr); // OK
core::ptr::write_volatile(&raw mut (*DEVICE_ADDR).count, regs.count); // OK
core::ptr::write_volatile(DEVICE_ADDR, regs);
//~^ volatile_composites
let _regs = MyDevRegisters {
baseaddr: core::ptr::read_volatile(&raw const (*DEVICE_ADDR).baseaddr), // OK
count: core::ptr::read_volatile(&raw const (*DEVICE_ADDR).count), // OK
};
let _regs = core::ptr::read_volatile(DEVICE_ADDR);
//~^ volatile_composites
}
// std::ptr::NonNull
unsafe {
let ptr = std::ptr::NonNull::new(DEVICE_ADDR).unwrap();
ptr.write_volatile(regs);
//~^ volatile_composites
let _regs = ptr.read_volatile();
//~^ volatile_composites
}
// Red herring
{
let thing = NonNull("hello".to_string());
thing.write_volatile(&"goodbye".into()); // OK
}
// Zero size types OK
unsafe {
struct Empty;
(0xdead as *mut Empty).write_volatile(Empty); // OK
// Note that this is OK because Wrapper<Empty> is itself ZST, not because of the repr transparent
// handling tested below.
(0xdead as *mut Wrapper<Empty>).write_volatile(Wrapper((), Empty, ())); // OK
}
// Via repr transparent newtype
unsafe {
(0xdead as *mut Wrapper<usize>).write_volatile(Wrapper((), 123, ())); // OK
(0xdead as *mut Wrapper<Wrapper<usize>>).write_volatile(Wrapper((), Wrapper((), 123, ()), ())); // OK
(0xdead as *mut Wrapper<MyDevRegisters>).write_volatile(Wrapper((), MyDevRegisters::default(), ()));
//~^ volatile_composites
}
// Plain type alias OK
unsafe {
type MyU64 = u64;
(0xdead as *mut MyU64).write_volatile(123); // OK
}
// Wide pointers are not OK as data
unsafe {
let things: &[u32] = &[1, 2, 3];
(0xdead as *mut *const u32).write_volatile(things.as_ptr()); // OK
let wideptr: *const [u32] = std::ptr::from_raw_parts(things.as_ptr(), things.len());
(0xdead as *mut *const [u32]).write_volatile(wideptr);
//~^ volatile_composites
}
// Plain pointers and pointers with lifetimes are OK
unsafe {
let v: u32 = 123;
let rv: &u32 = &v;
(0xdead as *mut &u32).write_volatile(rv); // OK
}
// C-style enums are OK
unsafe {
// Bad: need some specific repr
enum PlainEnum {
A = 1,
B = 2,
C = 3,
}
(0xdead as *mut PlainEnum).write_volatile(PlainEnum::A);
//~^ volatile_composites
// OK
#[repr(u32)]
enum U32Enum {
A = 1,
B = 2,
C = 3,
}
(0xdead as *mut U32Enum).write_volatile(U32Enum::A); // OK
// OK
#[repr(C)]
enum CEnum {
A = 1,
B = 2,
C = 3,
}
(0xdead as *mut CEnum).write_volatile(CEnum::A); // OK
// Nope
enum SumType {
A(String),
B(u32),
C,
}
(0xdead as *mut SumType).write_volatile(SumType::C);
//~^ volatile_composites
// A repr on a complex sum type is not good enough
#[repr(C)]
enum ReprSumType {
A(String),
B(u32),
C,
}
(0xdead as *mut ReprSumType).write_volatile(ReprSumType::C);
//~^ volatile_composites
}
// SIMD is OK
unsafe {
(0xdead as *mut std::simd::u32x4).write_volatile(std::simd::u32x4::splat(1)); // OK
}
// Can't see through generic wrapper
unsafe {
do_device_write::<MyDevRegisters>(0xdead as *mut _, Default::default()); // OK
}
let mut s = String::from("foo");
unsafe {
std::ptr::write_volatile(&mut s, String::from("bar"));
//~^ volatile_composites
}
}
// Generic OK
unsafe fn do_device_write<T>(ptr: *mut T, v: T) {
unsafe {
ptr.write_volatile(v); // OK
}
}