Skip to content

Unsoundness in get #150

@lwz23

Description

@lwz23

Hello, thank you for your contribution in this project, I an testing our static analysis tool in github's Rust project and I notice the following code:

pub struct AssetPtr<T> {
    ptr: *mut T,
}

impl<T> AssetPtr<T> {
    /// Attach to an existing iasset pointer without reference increment.
    fn attach(lp: *mut T) -> Self {
        assert!(!lp.is_null());
        Self {
            ptr: lp
        }
    }
    /// Attach to an iasset pointer and increment its reference count.
    pub fn adopt(lp: *mut T) -> Self {
        let mut me = Self::attach(lp);
        me.get().add_ref();
        me
    }
    /// Get as an iasset type.
    fn get(&mut self) -> &mut iasset {
        let ptr = self.ptr as *mut iasset;
        unsafe { &mut *ptr }
    }
}

The issue is that AssetPtr::adopt is a public function that accepts any type T, but internally it assumes T can be safely cast to an iasset. There's no trait bound or type checking to ensure this is valid.
The problematic code path is:

adopt takes any *mut T pointer
It calls get() which unsafely casts self.ptr to *mut iasset and dereferences it
It then calls add_ref() on the supposed iasset

Since there's no guarantee that T is actually an iasset or has the same memory layout, this creates undefined behavior when given a pointer to an incompatible type. The function provides a safe interface (no unsafe required to call it) but can lead to memory safety violations.
A valid path to call this fn: pub fn adopt -> fn get

POC

fn main() {
    // Create a struct that is NOT an iasset
    struct NotAnIAsset {
        some_field: u32,
    }
    
    // Create an instance of NotAnIAsset
    let not_iasset = Box::new(NotAnIAsset { some_field: 42 });
    
    // Convert the Box to a raw pointer
    let raw_ptr = Box::into_raw(not_iasset);
    
    // Call the public function with a pointer to something that is not an iasset
    let asset_ptr = AssetPtr::adopt(raw_ptr as *mut NotAnIAsset);
    
    // The above call will cause undefined behavior when it tries to:
    // 1. Cast the NotAnIAsset pointer to iasset in the get() method
    // 2. Call add_ref() on something that doesn't have that method
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions