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

Add lint against ambiguous wide pointer comparisons #117758

Merged
merged 5 commits into from Dec 11, 2023

Conversation

Urgau
Copy link
Contributor

@Urgau Urgau commented Nov 9, 2023

This PR is the resolution of #106447 decided in #117717 by T-lang.

ambiguous_wide_pointer_comparisons

warn-by-default

The ambiguous_wide_pointer_comparisons lint checks comparison of *const/*mut ?Sized as the operands.

Example

let ab = (A, B);
let a = &ab.0 as *const dyn T;
let b = &ab.1 as *const dyn T;

let _ = a == b;

Explanation

The comparison includes metadata which may not be expected.


This PR also drops clippy::vtable_address_comparisons which is superseded by this one.

One thing: is the current naming right? invalid seems a bit too much.

Fixes #117717

@rustbot
Copy link
Collaborator

rustbot commented Nov 9, 2023

r? @davidtwco

(rustbot has picked a reviewer for you, use r? to override)

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Nov 9, 2023
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@asquared31415
Copy link
Contributor

#117717 says that the lint should be on "non-thin pointers" where #106447 (comment) says specifically for "dyn trait pointer comparison".

I believe that the intent was only to lint on dyn trait, because it could be useful to compare the metadata portion of slice raw pointers. This does appear to be what is implemented, but I think it would be good to get clarification on exactly what was agreed on.

@Urgau Urgau force-pushed the lint_pointer_trait_comparisons branch from 2b66621 to 2627283 Compare November 9, 2023 18:14
@rustbot
Copy link
Collaborator

rustbot commented Nov 9, 2023

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

@asquared31415
Copy link
Contributor

Naming bikeshed since you mentioned that "invalid" seems strong: unreliable_dyn_pointer_comparisons.

I think the name should communicate that the comparison may spuriously return true or false and cannot be relied upon for correctness.

@rust-log-analyzer

This comment has been minimized.

@Urgau Urgau force-pushed the lint_pointer_trait_comparisons branch from 2627283 to 048ddcd Compare November 9, 2023 18:41
@Urgau
Copy link
Contributor Author

Urgau commented Nov 9, 2023

@scottmcm could you clarify the unclear-ness around the lint intention #117758 (comment) ?

As far as naming goes, it will depend on an answer to the previous question.

@rust-log-analyzer

This comment has been minimized.

@scottmcm scottmcm added the I-lang-nominated The issue / PR has been nominated for discussion during a lang team meeting. label Nov 9, 2023
@scottmcm
Copy link
Member

scottmcm commented Nov 9, 2023

Hmm, I think my personal vision of this was something like unclear_fat_pointer_comparisons that would lint on *const T for any T: ?Sized too, even without knowing what specific kind of DST it was. My thought was that even if you did intend to compare both (say because it's a slice and that's what you meant), it might still be good to use ptr::eq to help emphasize that you're not accidentally comparing the length too (like say if you forgot it was a slice pointer and not an item pointer).

But I'm not sure if that's what other people from the triage meeting were thinking. cc @Amanieu @rust-lang/lang @rust-lang/lang-advisors so others can chime in if they were thinking that this would check dyn only.

(Do people frequently compare slice pointers as a way of checking the length too? I don't know that I've ever seen code doing that, vs working in https://doc.rust-lang.org/nightly/std/primitive.pointer.html#method.len + data pointer separately...)

@asquared31415
Copy link
Contributor

The Reference says

When comparing raw pointers they are compared by their address, rather than by what they point to. When comparing raw pointers to dynamically sized types they also have their additional data compared.

The following code also executes without panicking:

fn main() {
    let arr = [1_u32, 2, 3, 4];
    let ptr = arr.as_ptr();
    let p1 = core::ptr::slice_from_raw_parts(ptr, 4);
    let p2 = core::ptr::slice_from_raw_parts(ptr, 3);
    // addresses are equal
    assert!(p1.cast::<()>() as usize == p2.cast::<()>() as usize);
    // pointers are not equal
    assert!(p1 != p2);
}

I think that it's reasonable to rely on the metadata being compared. The issue arises because *const dyn Trait metadata isn't consistent, so when the metadata is compared, the comparison might be meaningless for such types.

@asquared31415
Copy link
Contributor

asquared31415 commented Nov 9, 2023

Also there's not a stable way to get the length metadata of a slice pointer without asserting validity as a reference to 0 bytes (you can make a &[()] and access its length but only if the pointer is to a valid object), so I doubt that many users are using that method that you linked at all.

@scottmcm
Copy link
Member

scottmcm commented Nov 9, 2023

I think the name should communicate that the comparison may spuriously return true or false and cannot be relied upon for correctness.

Hmm, so I specifically wasn't thinking that this lint would be about "hey, vtable metadata comparison is unreliable", because if that's the goal, it needs to lint calls to https://doc.rust-lang.org/std/ptr/fn.eq.html too. But ptr::eq is explicitly documented to talk about that unreliability:

When comparing wide pointers, both the address and the metadata are tested for equality. However, note that comparing trait object pointers (*const dyn Trait) is unreliable: pointers to values of the same underlying type can compare inequal (because vtables are duplicated in multiple codegen units), and pointers to values of different underlying type can compare equal (since identical vtables can be deduplicated within a codegen unit).

And a "hey, vtable comparison doesn't do what you probably want" feels more like a clippy lint to me, because the only (stable) way past it is to #[allow] it if that is what you meant to do.

I see this lint as being about saying "hey, that's a fat pointer, and we know that fat pointer comparison can be surprising. Please think about what you meant -- maybe you meant to look at the metadata too, maybe you only meant the data address -- and use the function that corresponds to that so it's clear to the reader. We're fine with whichever one you meant, but would like you to clarify."

(Sortof like the direction we're going for things like u32::max_value as u64, where we want to be able to say "that's sketchy, please say which thing you meant, but if it's the address of the function as a u64 that's fine, we just want it to be obvious to the reader that that's actually what you meant".)

@Urgau Urgau force-pushed the lint_pointer_trait_comparisons branch from 048ddcd to 3da2841 Compare November 9, 2023 19:39
@rust-log-analyzer

This comment has been minimized.

@Urgau Urgau force-pushed the lint_pointer_trait_comparisons branch from 3da2841 to 2f4c313 Compare November 9, 2023 20:54
@rustbot
Copy link
Collaborator

rustbot commented Nov 9, 2023

The Miri subtree was changed

cc @rust-lang/miri

@rust-log-analyzer

This comment has been minimized.

@Urgau Urgau force-pushed the lint_pointer_trait_comparisons branch from 2f4c313 to 6175639 Compare November 9, 2023 21:25
@Urgau Urgau force-pushed the lint_pointer_trait_comparisons branch from 6175639 to 536eb97 Compare November 10, 2023 10:00
@rust-log-analyzer

This comment has been minimized.

@Urgau
Copy link
Contributor Author

Urgau commented Nov 10, 2023

Hmm, I think my personal vision of this was something like unclear_fat_pointer_comparisons that would lint on *const T for any T: ?Sized

In the light of this, I've adjusted the lint accordingly and renamed it as you suggested.

@Urgau Urgau force-pushed the lint_pointer_trait_comparisons branch from 536eb97 to 2b1bbe4 Compare November 10, 2023 10:09
@Urgau Urgau force-pushed the lint_pointer_trait_comparisons branch from 96be778 to 5e1bfb5 Compare December 6, 2023 13:02
@rfcbot rfcbot added finished-final-comment-period The final comment period is finished for this PR / Issue. to-announce Announce this issue on triage meeting and removed final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. labels Dec 9, 2023
@rfcbot
Copy link

rfcbot commented Dec 9, 2023

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

This will be merged soon.

@Urgau
Copy link
Contributor Author

Urgau commented Dec 9, 2023

@davidtwco Since you reviewed this PR, the scope of the lint grew a bit to lint on all unsized pointers as such the implementation changed quite a bit, in particular the second commit 5630540.

I would appreciate it if you could review those changes or delegate me bors permissions for this PR.

@davidtwco
Copy link
Member

@bors r+

@bors
Copy link
Contributor

bors commented Dec 11, 2023

📌 Commit 5e1bfb5 has been approved by davidtwco

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). labels Dec 11, 2023
@bors
Copy link
Contributor

bors commented Dec 11, 2023

⌛ Testing commit 5e1bfb5 with merge 8a37655...

@bors
Copy link
Contributor

bors commented Dec 11, 2023

☀️ Test successful - checks-actions
Approved by: davidtwco
Pushing 8a37655 to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Dec 11, 2023
@bors bors merged commit 8a37655 into rust-lang:master Dec 11, 2023
12 checks passed
@rustbot rustbot added this to the 1.76.0 milestone Dec 11, 2023
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (8a37655): comparison URL.

Overall result: no relevant changes - no action needed

@rustbot label: -perf-regression

Instruction count

This benchmark run did not return any relevant results for this metric.

Max RSS (memory usage)

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
2.9% [2.2%, 3.6%] 2
Improvements ✅
(primary)
-5.6% [-5.6%, -5.6%] 1
Improvements ✅
(secondary)
-1.7% [-2.1%, -1.3%] 2
All ❌✅ (primary) -5.6% [-5.6%, -5.6%] 1

Cycles

This benchmark run did not return any relevant results for this metric.

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 671.24s -> 672.509s (0.19%)
Artifact size: 314.20 MiB -> 314.21 MiB (0.00%)

@notriddle
Copy link
Contributor

This seems to be failing CI for unrelated pull requests?

#118402 (comment)

#118623 (comment)

#118833 (comment)

Testing GCC stage1 (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
   Compiling y v0.1.0 (/checkout/compiler/rustc_codegen_gcc/build_system)
    Finished release [optimized] target(s) in 1.43s
     Running `/checkout/obj/build/x86_64-unknown-linux-gnu/stage1-codegen/x86_64-unknown-linux-gnu/release/y test --use-system-gcc --use-backend gcc --out-dir /checkout/obj/build/x86_64-unknown-linux-gnu/stage1-tools/cg_gcc --release --no-default-features --mini-tests --std-tests`
Using system GCC
Using system GCC
error: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
##[error]   --> example/mini_core.rs:344:9
    |
    |
344 |         *self == *other
    |
    |
    = note: `-D ambiguous-wide-pointer-comparisons` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(ambiguous_wide_pointer_comparisons)]`
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
344 |         std::ptr::addr_eq(*self, *other)
help: use explicit `std::ptr::eq` method to compare metadata and addresses
    |
    |
344 |         std::ptr::eq(*self, *other)

error: ambiguous wide pointer comparison, the comparison includes metadata which may not be expected
##[error]   --> example/mini_core.rs:347:9
    |
    |
347 |         *self != *other
    |
help: use `std::ptr::addr_eq` or untyped pointers to only compare their addresses
    |
    |
347 |         !std::ptr::addr_eq(*self, *other)
help: use explicit `std::ptr::eq` method to compare metadata and addresses
    |
    |
347 |         !std::ptr::eq(*self, *other)

error: aborting due to 2 previous errors

@RalfJung
Copy link
Member

Testing GCC stage1

@GuillaumeGomez @antoyo any idea why this would land but then start failing?

@GuillaumeGomez
Copy link
Member

@Urgau proposed a theory about this here. I'm still surprised though.

bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 13, 2023
…ons, r=<try>

Add lint against function pointer comparisons

This is kind of a follow-up to rust-lang#117758 where we added a lint against wide pointer comparisons for being ambiguous and unreliable; well function pointer comparisons are also unreliable. We should IMO follow a similar logic and warn people about it.

-----

## `unpredictable_function_pointer_comparisons`

*warn-by-default*

The `unpredictable_function_pointer_comparisons` lint checks comparison of function pointer as the operands.

### Example

```rust
fn foo() {}
let a = foo as fn();

let _ = a == foo;
```

### Explanation

Function pointers comparisons do not produce meaningful result since they are never guaranteed to be unique and could vary between different code generation units. Furthermore different function could have the same address after being merged together.

----

This PR also uplift the very similar `clippy::fn_address_comparisons` lint, which only linted on if one of the operand was an `ty::FnDef` while this PR lints proposes to lint on all `ty::FnPtr` and `ty::FnDef`.

`@rustbot` labels +I-lang-nominated
@apiraino apiraino removed the to-announce Announce this issue on triage meeting label Dec 14, 2023
flip1995 pushed a commit to flip1995/rust that referenced this pull request Dec 16, 2023
…, r=davidtwco

Add lint against ambiguous wide pointer comparisons

This PR is the resolution of rust-lang#106447 decided in rust-lang#117717 by T-lang.

## `ambiguous_wide_pointer_comparisons`

*warn-by-default*

The `ambiguous_wide_pointer_comparisons` lint checks comparison of `*const/*mut ?Sized` as the operands.

### Example

```rust
let ab = (A, B);
let a = &ab.0 as *const dyn T;
let b = &ab.1 as *const dyn T;

let _ = a == b;
```

### Explanation

The comparison includes metadata which may not be expected.

-------

This PR also drops `clippy::vtable_address_comparisons` which is superseded by this one.

~~One thing: is the current naming right? `invalid` seems a bit too much.~~

Fixes rust-lang#117717
bjorn3 pushed a commit to bjorn3/rust that referenced this pull request Dec 19, 2023
…, r=davidtwco

Add lint against ambiguous wide pointer comparisons

This PR is the resolution of rust-lang#106447 decided in rust-lang#117717 by T-lang.

## `ambiguous_wide_pointer_comparisons`

*warn-by-default*

The `ambiguous_wide_pointer_comparisons` lint checks comparison of `*const/*mut ?Sized` as the operands.

### Example

```rust
let ab = (A, B);
let a = &ab.0 as *const dyn T;
let b = &ab.1 as *const dyn T;

let _ = a == b;
```

### Explanation

The comparison includes metadata which may not be expected.

-------

This PR also drops `clippy::vtable_address_comparisons` which is superseded by this one.

~~One thing: is the current naming right? `invalid` seems a bit too much.~~

Fixes rust-lang#117717
wip-sync pushed a commit to NetBSD/pkgsrc-wip that referenced this pull request Feb 18, 2024
Pkgsrc changes:
 * Adapt checksums and patches.

Upstream chnages:

Version 1.76.0 (2024-02-08)
==========================

Language
--------
- [Document Rust ABI compatibility between various types]
  (rust-lang/rust#115476)
- [Also: guarantee that char and u32 are ABI-compatible]
  (rust-lang/rust#118032)
- [Warn against ambiguous wide pointer comparisons]
  (rust-lang/rust#117758)

Compiler
--------
- [Lint pinned `#[must_use]` pointers (in particular, `Box<T>`
  where `T` is `#[must_use]`) in `unused_must_use`.]
  (rust-lang/rust#118054)
- [Soundness fix: fix computing the offset of an unsized field in
  a packed struct]
  (rust-lang/rust#118540)
- [Soundness fix: fix dynamic size/align computation logic for
  packed types with dyn Trait tail]
  (rust-lang/rust#118538)
- [Add `$message_type` field to distinguish json diagnostic outputs]
  (rust-lang/rust#115691)
- [Enable Rust to use the EHCont security feature of Windows]
  (rust-lang/rust#118013)
- [Add tier 3 {x86_64,i686}-win7-windows-msvc targets]
  (rust-lang/rust#118150)
- [Add tier 3 aarch64-apple-watchos target]
  (rust-lang/rust#119074)
- [Add tier 3 arm64e-apple-ios & arm64e-apple-darwin targets]
  (rust-lang/rust#115526)

Refer to Rust's [platform support page][platform-support-doc]
for more information on Rust's tiered platform support.

Libraries
---------
- [Add a column number to `dbg!()`]
  (rust-lang/rust#114962)
- [Add `std::hash::{DefaultHasher, RandomState}` exports]
  (rust-lang/rust#115694)
- [Fix rounding issue with exponents in fmt]
  (rust-lang/rust#116301)
- [Add T: ?Sized to `RwLockReadGuard` and `RwLockWriteGuard`'s Debug impls.]
  (rust-lang/rust#117138)
- [Windows: Allow `File::create` to work on hidden files]
  (rust-lang/rust#116438)

Stabilized APIs
---------------
- [`Arc::unwrap_or_clone`]
  (https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.unwrap_or_clone)
- [`Rc::unwrap_or_clone`]
  (https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.unwrap_or_clone)
- [`Result::inspect`]
  (https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.inspect)
- [`Result::inspect_err`]
  (https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.inspect_err)
- [`Option::inspect`]
  (https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.inspect)
- [`type_name_of_val`]
  (https://doc.rust-lang.org/stable/std/any/fn.type_name_of_val.html)
- [`std::hash::{DefaultHasher, RandomState}`]
  (https://doc.rust-lang.org/stable/std/hash/index.html#structs)
  These were previously available only through `std::collections::hash_map`.
- [`ptr::{from_ref, from_mut}`]
  (https://doc.rust-lang.org/stable/std/ptr/fn.from_ref.html)
- [`ptr::addr_eq`](https://doc.rust-lang.org/stable/std/ptr/fn.addr_eq.html)

Cargo
-----

See [Cargo release notes]
(https://github.com/rust-lang/cargo/blob/master/CHANGELOG.md#cargo-176-2024-02-08).

Rustdoc
-------
- [Don't merge cfg and doc(cfg) attributes for re-exports]
  (rust-lang/rust#113091)
- [rustdoc: allow resizing the sidebar / hiding the top bar]
  (rust-lang/rust#115660)
- [rustdoc-search: add support for traits and associated types]
  (rust-lang/rust#116085)
- [rustdoc: Add highlighting for comments in items declaration]
  (rust-lang/rust#117869)

Compatibility Notes
-------------------
- [Add allow-by-default lint for unit bindings]
  (rust-lang/rust#112380)
  This is expected to be upgraded to a warning by default in a future Rust
  release. Some macros emit bindings with type `()` with user-provided spans,
  which means that this lint will warn for user code.
- [Remove x86_64-sun-solaris target.]
  (rust-lang/rust#118091)
- [Remove asmjs-unknown-emscripten target]
  (rust-lang/rust#117338)
- [Report errors in jobserver inherited through environment variables]
  (rust-lang/rust#113730)
  This [may warn](rust-lang/rust#120515)
  on benign problems too.
- [Update the minimum external LLVM to 16.]
  (rust-lang/rust#117947)
- [Improve `print_tts`](rust-lang/rust#114571)
  This change can break some naive manual parsing of token trees
  in proc macro code which expect a particular structure after
  `.to_string()`, rather than just arbitrary Rust code.
- [Make `IMPLIED_BOUNDS_ENTAILMENT` into a hard error from a lint]
  (rust-lang/rust#117984)
- [Vec's allocation behavior was changed when collecting some iterators]
  (rust-lang/rust#110353)
  Allocation behavior is currently not specified, nevertheless
  changes can be surprising.
  See [`impl FromIterator for Vec`]
  (https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#impl-FromIterator%3CT%3E-for-Vec%3CT%3E)
  for more details.
- [Properly reject `default` on free const items]
  (rust-lang/rust#117818)
@dtolnay dtolnay removed the I-libs-api-nominated The issue / PR has been nominated for discussion during a libs-api team meeting. label Mar 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Lint on pointer comparisons for non-thin pointers.