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

trivially_copy_pass_by_ref does not consider pointers #5953

Closed
antonok-edm opened this issue Aug 24, 2020 · 3 comments · Fixed by #8639
Closed

trivially_copy_pass_by_ref does not consider pointers #5953

antonok-edm opened this issue Aug 24, 2020 · 3 comments · Fixed by #8639
Labels
C-bug Category: Clippy is not doing the correct thing E-help-wanted Call for participation: Help is requested to fix this issue. E-medium Call for participation: Medium difficulty level problem and requires some initial experience. I-false-positive Issue: The lint was triggered on code it shouldn't have

Comments

@antonok-edm
Copy link

This is a similar issue to #2946.

Recently it was discovered that a change recommended by the trivially_copy_pass_by_ref lint had started causing undefined behavior in Rust-SDL2: Rust-SDL2/rust-sdl2#1020.

This is the problematic change:

 impl Point {
     // ...
-    pub fn raw(&self) -> *const sys::SDL_Point {
+    pub fn raw(self) -> *const sys::SDL_Point {
         self.raw
     }
 }

Point is a simple wrapper around an owned sys::SDL_Point, which itself is just a simple FFI-compatible data structure. Since Point implements Copy, the trivially_copy_pass_by_ref lint was recommended on Point::raw - even though making that change causes Point::raw to return a pointer to an immediately-dropped copy of one of its own fields.

Here's a minimal reproduction of the issue. The Point::raw method correctly produces a consistent pointer to the internal RawPoint, whereas the Point::raw_linted method produces a different pointer on each call.

#![forbid(clippy::pedantic)]

#[derive(Clone, Copy)]
struct RawPoint {
    pub x: u8,
}

#[derive(Clone, Copy)]
struct Point {
    pub raw: RawPoint,
}

impl Point {
    pub fn raw(&self) -> *const RawPoint {
        &self.raw
    }

    pub fn raw_linted(self) -> *const RawPoint {
        &self.raw
    }
}

fn main() {
    let p = Point { raw: RawPoint { x: 10 } };

    // This passes
    assert_eq!(p.raw(), p.raw());
    // This fails
    assert_eq!(p.raw_linted(), p.raw_linted());
}

This would be a bit more challenging to decide than the solution in #2951. Pointers could be present as arbitrarily nested fields of a returned struct, which would not itself be a pointer. Further, pointers could be manipulated by long-lasting side effects (e.g. sent over a channel) during a function without actually ever being returned. Arguably it is the responsibility of the author of the unsafe dereference site to guarantee that the pointer remains valid, but it's still unreasonable to recommend this change in these cases.

Meta

  • cargo clippy -V: clippy 0.0.212 (346aec9 2020-07-11)
  • rustc -Vv:
    rustc 1.46.0-nightly (346aec9b0 2020-07-11)
    binary: rustc
    commit-hash: 346aec9b02f3c74f3fce97fd6bda24709d220e49
    commit-date: 2020-07-11
    host: x86_64-unknown-linux-gnu
    release: 1.46.0-nightly
    LLVM version: 10.0
    
@antonok-edm antonok-edm added the C-bug Category: Clippy is not doing the correct thing label Aug 24, 2020
antonok-edm referenced this issue in Rust-SDL2/rust-sdl2 Aug 24, 2020
@flip1995 flip1995 added E-medium Call for participation: Medium difficulty level problem and requires some initial experience. E-help-wanted Call for participation: Help is requested to fix this issue. labels Aug 24, 2020
@ghost
Copy link

ghost commented Aug 25, 2020

Just to make this clear. The fix is to check if the return type contains a pointer on any level and avoid linting if it does. Right?

@antonok-edm
Copy link
Author

@mikerite That should work in most(?) cases, including the original one. But there's still a lot of complexity when considering the potential for something like this, where the pointer is returned indirectly through some arbitrary amount of structural nesting:

struct PointerWrapper(*const RawPoint);

impl Point {
    fn raw_wrapped(&self) -> PointerWrapper {
        PointerWrapper(&self.raw)
    }
}

Or even this, where the pointer is not returned at all, rather it's used elsewhere by a side-effect:

impl Point {
    fn send_raw(&self, channel: &std::sync::mpsc::Sender) {
        channel.send(&self.raw).unwrap();
    }
}

@danielrh
Copy link

An alternative is to remove or disable the linter altogether until a safe approach can be determined?

@phansch phansch added the I-false-positive Issue: The lint was triggered on code it shouldn't have label Dec 18, 2020
rail-rain added a commit to rail-rain/rust-clippy that referenced this issue Apr 12, 2021
rail-rain added a commit to rail-rain/rust-clippy that referenced this issue Apr 12, 2021
rail-rain added a commit to rail-rain/rust-clippy that referenced this issue Apr 12, 2021
bors added a commit that referenced this issue Apr 12, 2021
Add a note on the issue #5953

Hello,

I thought it would be better to have a note and warning about this issue considering it introduced an UB in the past even with the "Search on Github" feature.

---

changelog: Add a note on the issue #5953 to the known problems section.
@bors bors closed this as completed in 373bb57 Jun 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: Clippy is not doing the correct thing E-help-wanted Call for participation: Help is requested to fix this issue. E-medium Call for participation: Medium difficulty level problem and requires some initial experience. I-false-positive Issue: The lint was triggered on code it shouldn't have
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants