Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

unsound trait impl on union #8946

Open
asomers opened this issue Jun 4, 2022 · 1 comment
Open

unsound trait impl on union #8946

asomers opened this issue Jun 4, 2022 · 1 comment
Labels
A-lint Area: New lints

Comments

@asomers
Copy link

asomers commented Jun 4, 2022

What it does

Warns if a trait is manually implemented on a union type, if it's not safe to do so.

For example

#[repr(C)]
pub union Foo {
    bar: u32,
    baz: u64
}
impl PartialEq on Foo {
    fn partial_eq(&self, &other: Self) -> bool {
        // Unsound!  The baz field may be uninitialized data, if either argument was initialized like
        // let x = Foo{bar: 42}
        self.baz == other.baz
    }
}

A common pattern for using unions is to initialize only the fields one intends to use. But there's no way for most trait impls to know which fields those are. Accessing an uninitialized field is undefined behavior. Even if the entire object was zero-initialized like let mut x: Foo = mem::zeroed(); x.bar =42, it's still not possible to correctly implement PartialEq, because the implementation doesn't know which fields "matter". For example:

let mut x = Foo{baz: 0x1111111111111111};
let mut y = Foo{baz: 0x2222222222222222};
x.bar = 42;
y.bar = 42;
assert_eq!(x, y);

In this example, logically x should equal y. But any PartialEq implementation would disagree, if it checks all of the struct's bits.
Similar arguments could be made for Hash, PartialOrd, and even Debug.

Lint Name

unsound_trait_impl_on_union

Category

correctness

Advantage

It will prevent undefined behavior, and prevent incorrect behavior of basic operations like ==. Such a lint could've caught bugs like rust-lang/libc#2816 .

Drawbacks

Occasionally it may actually be ok to implement these traits on a union, for example when every member has the same size.

Example

#[repr(C)]
pub union Foo {
    bar: u32,
    baz: u64
}
impl PartialEq on Foo {
    fn partial_eq(&self, &other: Self) -> bool {
        // Unsound!  The baz field may be uninitialized data, if either argument was initialized like
        // let x = Foo{bar: 42}
        self.baz == other.baz
    }
}

The PartialEq implementation should be omitted. There's simply no way to do it correctly.

@asomers asomers added the A-lint Area: New lints label Jun 4, 2022
@Jarcho
Copy link
Contributor

Jarcho commented Jun 4, 2022

The category should be suspicious rather than correctness unless the implementation is examined. There are enough cases where it is sound.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lint Area: New lints
Projects
None yet
Development

No branches or pull requests

2 participants