Skip to content

unsafe { ffi(generic_bytes.as_ref().as_ptr(), generic_bytes.as_ref().len()) } trusts AsRef impl #10323

Description

@niklasf

What it does

Consider the example below. example() is (very likely, depending on ffi()) unsound. The problem is that user-provided AsRef implementations could use interior mutability, so that the two .as_ref() calls unexpectedly return different slices.

fn example(bytes: impl AsRef<[u8]>) {
    unsafe {
        ffi(bytes.as_ref().as_ptr(), bytes.as_ref().len())
    }
}
#[derive(Default)]
struct Evil {
    toggle: AtomicUsize,
}

impl AsRef<[u8]> for Evil {
    fn as_ref(&self) -> &[u8] {
        if self.toggle.fetch_xor(1, Ordering::Relaxed) == 0 {
            b"hi"
        } else {
            b"there"
        }
    }
}

To be fair, the example is quite contrived, and AsRef is intended for cheap reference to reference conversions, so that real-world implementations with interior mutability are unlikely to exist.

Lint Name

trusted_asref

Category

correctness, suspicious

Advantage

Checking examples found via https://github.com/search?q=language%3Arust+as_ref%28%29.as_ptr%28%29&type=code, this appears to be a common soundness issue.

Drawbacks

There may be false positives, as we cannot know what ffi() actually does.

Example

fn example(bytes: impl AsRef<[u8]>) {
    unsafe {
        ffi(bytes.as_ref().as_ptr(), bytes.as_ref().len())
    }
}

Could be written as:

fn example(bytes: impl AsRef<[u8]>) {
    let bytes = bytes.as_ref();
    unsafe {
        ffi(bytes.as_ptr(), bytes.len())
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-lintArea: New lints

    Type

    No type

    Fields

    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