Writing to `*mut u8` from immutable context

83 views Asked by At

I have a struct containing a raw pointer to data I'm getting from somewhere else (in this case, a memory-mapped file opened through OpenFileMappingA CreateFileMappingA which I'll need to write for IPC purposes, after mapping it through MapViewOfFile. Linking the C++ docs because the Rust ones only provide the signature. For brevity I'll skip the calls to MapViewOfFile and checking error statuses; the actual code is handling this. But the compiler is currently allowing me to use a *const u8 or *mut u8 struct field in a non-mutably borrowed reference for writing to. As a sanity check, I'd love to know:

  • is this still sound code?
  • is this safe to do when the source of my data provides me a *mut c_void?
  • are there any safety and soundness pitfalls other than null pointers and uninitialized data?

This is not a question about mutating some immutable ref I don't own. Somehow all "similar" questions SO provide are about coercing immutable to mutable, which isn't the case here.

use windows::Win32::System::Memory::MEMORY_MAPPED_VIEW_ADDRESS as MmfAddr;
// omit other uses

pub struct MemMappedFile {
    mmf_ptr: MmfAddr, // field Value is a *mut c_void
    other_ptr: *const u8, // the first N bytes of the above pointer are used for metadata
    opened: bool,
    // Other fields for safety and sanity handling
}

impl MemMappedFile {
    pub fn write_bytes(&self, bytes: &[u8]) -> Result<(), windows_core::Error> {
        // omit sanity checks -- real code errors if bytes.len() > 
        let count = bytes.len();
        let byte_ptr = bytes.as_ptr();
        unsafe { std::ptr::copy(byte_ptr, self.mmf_ptr.Value.cast(), count)};
        windows::Win32::Foundation::GetLastError()?
    }

    pub fn write2(&self, bytes: &[u8]) {
        // omit checks here too
        let count = bytes.len();
        let byte_ptr = bytes.as_ptr();
        unsafe { std::ptr::copy(byte_ptr, self.other_ptr.cast_mut(), count)};
    }

    pub fn magical_constructor(/* args go here */) -> Result<Self, windows_core::Error> {
        // Omit opening and mapping the file and whatnot, this creates the pointers
        // and all pointers are guaranteed to be valid.
        // I also make sure to zero out all bytes in the mmf before instantiating Self
    }
}

I'd, quite frankly, expect the compiler to throw a fit on either the writes (&self is not mut) or on the cast_mut() on the *const u8 for the same reasons.

This compiles and seems to work (in non-IPC reads and writes anyway) but looks fishy; I know I'm using an explicitly mutable pointer in one case, but Rust is usually very strict and I've never had to use as much unsafe code as the windows-rs crate is asking me to use (there doesn't seem to be COM or WinRT interfaces for this and I need a fast way to share a few MB between applications on the same host)


Additional info thanks to an insightful comment:
Named Shared memory is created using the invalid filehandle and namespaces, see the relevant docs for more info. All of these functions are exposed through the windows-rs crate (mostly marked as unsafe) and plenty of them return Results. Not to mention one could call GetLastError() which is set from the OS side of things and is translated into a Rust implementation of the errors. (beware with that function though; the error enum contains OK which tripped me up)

0

There are 0 answers