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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions