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

Warn on references casting to bigger memory layout #118983

Merged
merged 4 commits into from Feb 13, 2024

Conversation

Urgau
Copy link
Contributor

@Urgau Urgau commented Dec 15, 2023

This PR extends the invalid_reference_casting lint (deny-by-default) which currently lint on &T -> &mut T casting to also lint on &(mut) A -> &(mut) B where size_of::<B>() > size_of::<A>() (bigger memory layout requirement).

The goal is to detect such cases:

let u8_ref: &u8 = &0u8;
let u64_ref: &u64 = unsafe { &*(u8_ref as *const u8 as *const u64) };
//~^ ERROR casting references to a bigger memory layout is undefined behavior

let mat3 = Mat3 { a: Vec3(0i32, 0, 0), b: Vec3(0, 0, 0), c: Vec3(0, 0, 0) };
let mat3 = unsafe { &*(&mat3 as *const _ as *const [[i64; 3]; 3]) };
//~^ ERROR casting references to a bigger memory layout is undefined behavior

This is added to help people who write unsafe code, especially when people have matrix struct that they cast to simple array of arrays.

EDIT: One caveat, due to the &Header uncertainty the lint only fires when it can find the underline allocation.

I have manually tested all the new expressions that warn against Miri, and they all report immediate UB.

r? @est31

@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 Dec 15, 2023
@rust-log-analyzer

This comment has been minimized.

@Nilstrieb
Copy link
Member

Whether this is actually always UB is subject to debate, also called the "&Header issue".
rust-lang/unsafe-code-guidelines#256. SB forbids it, TB allows it, but from what I've gathered, consensus seems to be that it should be allowed, probably.
It is or course UB to cast a &u8 to a &u32 if the backing allocation is only 1 byte, but this lint cannot know how big the backing allocation is. Given that it's a deny by default lint, we probably want to be careful with false positives.
I'm not sure what to do though, just commenting that this pattern may not always be UB.

@Urgau Urgau force-pushed the invalid_ref_casting-bigger-layout branch from 7e737d4 to dbf8ac9 Compare December 15, 2023 15:38
@Urgau
Copy link
Contributor Author

Urgau commented Dec 15, 2023

Hum, this is surprising. This may not be as obviously UB (or not) as I though and expected.

If this happens to not be (for now) undecided, I can probably change the implementation to find the backing allocation, if possible of course.

cc @RalfJung, to have an opsem expert opinion on this.

@rust-log-analyzer

This comment has been minimized.

@Urgau Urgau marked this pull request as draft December 15, 2023 16:15
@Urgau
Copy link
Contributor Author

Urgau commented Dec 15, 2023

@Nilstrieb rust-lang/unsafe-code-guidelines#256 seems to be about the size component (read layout size) only? What about the alignment? Casting to reference to reference that requires more alignment, is this UB?

@Nilstrieb
Copy link
Member

Creating an unaligned reference is always UB, but you only know at runtime whether the source reference will be aligned or not.

@RalfJung
Copy link
Member

@Nilstrieb is right. Such a cast is certainly highly suspect, but may or may not always be UB. Tree Borrows allows such casts under certain conditions.

@Urgau
Copy link
Contributor Author

Urgau commented Dec 17, 2023

@RalfJung I suspect that none of those cases can be trivial determined? And that in it's current form this PR cannot make progress until we have an approved/official aliasing model?

@Nilstrieb
Copy link
Member

These cases can indeed not be determined trivially, knowing whether the allocation is bigger than the referent is very non-local.

@Urgau Urgau force-pushed the invalid_ref_casting-bigger-layout branch from dbf8ac9 to e113cbb Compare December 17, 2023 13:00
@rust-log-analyzer

This comment has been minimized.

@Urgau Urgau force-pushed the invalid_ref_casting-bigger-layout branch from e113cbb to e8d7b9d Compare December 17, 2023 13:39
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@Urgau Urgau force-pushed the invalid_ref_casting-bigger-layout branch from af355bf to f5e5e9f Compare December 17, 2023 16:52
@rust-log-analyzer

This comment has been minimized.

@Urgau Urgau force-pushed the invalid_ref_casting-bigger-layout branch from b3b4571 to 3b0b09e Compare December 18, 2023 10:25
@Urgau Urgau marked this pull request as ready for review December 18, 2023 10:26
@rustbot
Copy link
Collaborator

rustbot commented Dec 18, 2023

The Miri subtree was changed

cc @rust-lang/miri

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

@Urgau
Copy link
Contributor Author

Urgau commented Dec 18, 2023

I've updated the implementation to only lint when we can find the allocation, that means that it only works locally. The implementation tries to follow the reference at the start of the casting chain to find those patterns, let a = 12; or let a = foo();.

This should resolve the concern from @Nilstrieb and @RalfJung.

@est31 this is ready for review

@RalfJung
Copy link
Member

Is rustc the right place for such a specific lint or should this go to clippy?

@asquared31415
Copy link
Contributor

Tests that make sure the lint is conservative with regards to certain patterns that may be accepted in the future would be nice to have.

@Urgau
Copy link
Contributor Author

Urgau commented Dec 18, 2023

Is rustc the right place for such a specific lint or should this go to clippy?

I think so. We should warn/deny code that is UB as soon as possible, not wait for people to write their code, clean it up and then run clippy to find out that it is actually not OK.

(Not to mention that many people don't use clippy).

EDIT: Note that this PR does not add any new lint, it just extends the invalid_reference_casting lint that already lints on &T to &mut T casting.

Tests that make sure the lint is conservative with regards to certain patterns that may be accepted in the future would be nice to have.

Do you have such pattern in mind? I've already restricted the scope to exclude the &Header issue (rust-lang/unsafe-code-guidelines#256).

@est31
Copy link
Member

est31 commented Dec 19, 2023

In general, I think there is a place for lints that find instances of code that is obvious true undefined behaviour. The emphasis here lies on obvious: there have to be zero false positives. That's a much higher bar than what many clippy lints need to pass.

If the compiler tells the user that some pattern is UB, then the user will believe that much more likely than if clippy said it.

That doesn't mean that this particular PR is automatically a good idea. There need to be thorough tests to ensure that there is no false positives. Also, there is many ways that code can be UB, and we cannot write and maintain heuristics for all possible ways: we need to pick the instances which we think are easiest to maintain and that catch the most UB "in the wild".

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 12, 2024
@Urgau Urgau force-pushed the invalid_ref_casting-bigger-layout branch from 3b0b09e to e08c9d1 Compare February 12, 2024 18:40
@oli-obk
Copy link
Contributor

oli-obk commented Feb 12, 2024

@bors r+

@bors
Copy link
Contributor

bors commented Feb 12, 2024

📌 Commit e08c9d1 has been approved by oli-obk

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-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Feb 12, 2024
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Feb 12, 2024
…ayout, r=oli-obk

Warn on references casting to bigger memory layout

This PR extends the [`invalid_reference_casting`](https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#invalid-reference-casting) lint (*deny-by-default*) which currently lint on `&T -> &mut T` casting to also lint on `&(mut) A -> &(mut) B` where `size_of::<B>() > size_of::<A>()` (bigger memory layout requirement).

The goal is to detect such cases:

```rust
let u8_ref: &u8 = &0u8;
let u64_ref: &u64 = unsafe { &*(u8_ref as *const u8 as *const u64) };
//~^ ERROR casting references to a bigger memory layout is undefined behavior

let mat3 = Mat3 { a: Vec3(0i32, 0, 0), b: Vec3(0, 0, 0), c: Vec3(0, 0, 0) };
let mat3 = unsafe { &*(&mat3 as *const _ as *const [[i64; 3]; 3]) };
//~^ ERROR casting references to a bigger memory layout is undefined behavior
```

This is added to help people who write unsafe code, especially when people have matrix struct that they cast to simple array of arrays.

EDIT: One caveat, due to the [`&Header`](rust-lang/unsafe-code-guidelines#256) uncertainty the lint only fires when it can find the underline allocation.

~~I have manually tested all the new expressions that warn against Miri, and they all report immediate UB.~~

r? `@est31`
bors added a commit to rust-lang-ci/rust that referenced this pull request Feb 12, 2024
…iaskrgr

Rollup of 10 pull requests

Successful merges:

 - rust-lang#118983 (Warn on references casting to bigger memory layout)
 - rust-lang#119451 (Gate PR CI on clippy correctness lints)
 - rust-lang#120273 (compiletest: few naive improvements)
 - rust-lang#120889 (Implement Instant for UEFI)
 - rust-lang#120938 (Implement sys/thread for UEFI)
 - rust-lang#120950 (Fix async closures in CTFE)
 - rust-lang#120958 (Dejargonize `subst`)
 - rust-lang#120965 (Add lahfsahf and prfchw target feature)
 - rust-lang#120970 (add another test for promoteds-in-static)
 - rust-lang#120979 (Update books)

Failed merges:

 - rust-lang#120973 (allow static_mut_ref in some tests that specifically test mutable statics)

r? `@ghost`
`@rustbot` modify labels: rollup
bors added a commit to rust-lang-ci/rust that referenced this pull request Feb 13, 2024
…iaskrgr

Rollup of 8 pull requests

Successful merges:

 - rust-lang#118983 (Warn on references casting to bigger memory layout)
 - rust-lang#119451 (Gate PR CI on clippy correctness lints)
 - rust-lang#120273 (compiletest: few naive improvements)
 - rust-lang#120950 (Fix async closures in CTFE)
 - rust-lang#120958 (Dejargonize `subst`)
 - rust-lang#120965 (Add lahfsahf and prfchw target feature)
 - rust-lang#120970 (add another test for promoteds-in-static)
 - rust-lang#120979 (Update books)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 74f5e1f into rust-lang:master Feb 13, 2024
11 checks passed
@rustbot rustbot added this to the 1.78.0 milestone Feb 13, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Feb 13, 2024
Rollup merge of rust-lang#118983 - Urgau:invalid_ref_casting-bigger-layout, r=oli-obk

Warn on references casting to bigger memory layout

This PR extends the [`invalid_reference_casting`](https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#invalid-reference-casting) lint (*deny-by-default*) which currently lint on `&T -> &mut T` casting to also lint on `&(mut) A -> &(mut) B` where `size_of::<B>() > size_of::<A>()` (bigger memory layout requirement).

The goal is to detect such cases:

```rust
let u8_ref: &u8 = &0u8;
let u64_ref: &u64 = unsafe { &*(u8_ref as *const u8 as *const u64) };
//~^ ERROR casting references to a bigger memory layout is undefined behavior

let mat3 = Mat3 { a: Vec3(0i32, 0, 0), b: Vec3(0, 0, 0), c: Vec3(0, 0, 0) };
let mat3 = unsafe { &*(&mat3 as *const _ as *const [[i64; 3]; 3]) };
//~^ ERROR casting references to a bigger memory layout is undefined behavior
```

This is added to help people who write unsafe code, especially when people have matrix struct that they cast to simple array of arrays.

EDIT: One caveat, due to the [`&Header`](rust-lang/unsafe-code-guidelines#256) uncertainty the lint only fires when it can find the underline allocation.

~~I have manually tested all the new expressions that warn against Miri, and they all report immediate UB.~~

r? ``@est31``
@Urgau Urgau deleted the invalid_ref_casting-bigger-layout branch February 13, 2024 17:52
GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this pull request Feb 15, 2024
…iler-errors

Ignore unsized types when trying to determine the size of the original type

Fixes rust-lang#121074 a regression from rust-lang#118983
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Feb 15, 2024
Rollup merge of rust-lang#121104 - Urgau:bigger_layout-fix-fp, r=compiler-errors

Ignore unsized types when trying to determine the size of the original type

Fixes rust-lang#121074 a regression from rust-lang#118983
flip1995 pushed a commit to flip1995/rust that referenced this pull request Feb 26, 2024
…ayout, r=oli-obk

Warn on references casting to bigger memory layout

This PR extends the [`invalid_reference_casting`](https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#invalid-reference-casting) lint (*deny-by-default*) which currently lint on `&T -> &mut T` casting to also lint on `&(mut) A -> &(mut) B` where `size_of::<B>() > size_of::<A>()` (bigger memory layout requirement).

The goal is to detect such cases:

```rust
let u8_ref: &u8 = &0u8;
let u64_ref: &u64 = unsafe { &*(u8_ref as *const u8 as *const u64) };
//~^ ERROR casting references to a bigger memory layout is undefined behavior

let mat3 = Mat3 { a: Vec3(0i32, 0, 0), b: Vec3(0, 0, 0), c: Vec3(0, 0, 0) };
let mat3 = unsafe { &*(&mat3 as *const _ as *const [[i64; 3]; 3]) };
//~^ ERROR casting references to a bigger memory layout is undefined behavior
```

This is added to help people who write unsafe code, especially when people have matrix struct that they cast to simple array of arrays.

EDIT: One caveat, due to the [`&Header`](rust-lang/unsafe-code-guidelines#256) uncertainty the lint only fires when it can find the underline allocation.

~~I have manually tested all the new expressions that warn against Miri, and they all report immediate UB.~~

r? ``@est31``
@Urgau Urgau mentioned this pull request Mar 30, 2024
@saethlin
Copy link
Member

saethlin commented May 4, 2024

Why was this FCP approved? The wording in the lint as reported in #124685 is misleading at best. @traviscross are there any notes from the meeting that I can review? I can't really figure out what lang approved or discussed for this change.

@Urgau
Copy link
Contributor Author

Urgau commented May 4, 2024

wip-sync pushed a commit to NetBSD/pkgsrc-wip that referenced this pull request May 4, 2024
Pkgsrc changes:
 * Adapt checksums and patches, some have beene intregrated upstream.

Upstream chnages:

Version 1.78.0 (2024-05-02)
===========================

Language
--------
- [Stabilize `#[cfg(target_abi = ...)]`]
  (rust-lang/rust#119590)
- [Stabilize the `#[diagnostic]` namespace and
  `#[diagnostic::on_unimplemented]` attribute]
  (rust-lang/rust#119888)
- [Make async-fn-in-trait implementable with concrete signatures]
  (rust-lang/rust#120103)
- [Make matching on NaN a hard error, and remove the rest of
  `illegal_floating_point_literal_pattern`]
  (rust-lang/rust#116284)
- [static mut: allow mutable reference to arbitrary types, not just
  slices and arrays]
  (rust-lang/rust#117614)
- [Extend `invalid_reference_casting` to include references casting
  to bigger memory layout]
  (rust-lang/rust#118983)
- [Add `non_contiguous_range_endpoints` lint for singleton gaps
  after exclusive ranges]
  (rust-lang/rust#118879)
- [Add `wasm_c_abi` lint for use of older wasm-bindgen versions]
  (rust-lang/rust#117918)
  This lint currently only works when using Cargo.
- [Update `indirect_structural_match` and `pointer_structural_match`
  lints to match RFC]
  (rust-lang/rust#120423)
- [Make non-`PartialEq`-typed consts as patterns a hard error]
  (rust-lang/rust#120805)
- [Split `refining_impl_trait` lint into `_reachable`, `_internal` variants]
  (rust-lang/rust#121720)
- [Remove unnecessary type inference when using associated types
  inside of higher ranked `where`-bounds]
  (rust-lang/rust#119849)
- [Weaken eager detection of cyclic types during type inference]
  (rust-lang/rust#119989)
- [`trait Trait: Auto {}`: allow upcasting from `dyn Trait` to `dyn Auto`]
  (rust-lang/rust#119338)

Compiler
--------

- [Made `INVALID_DOC_ATTRIBUTES` lint deny by default]
  (rust-lang/rust#111505)
- [Increase accuracy of redundant `use` checking]
  (rust-lang/rust#117772)
- [Suggest moving definition if non-found macro_rules! is defined later]
  (rust-lang/rust#121130)
- [Lower transmutes from int to pointer type as gep on null]
  (rust-lang/rust#121282)

Target changes:

- [Windows tier 1 targets now require at least Windows 10]
  (rust-lang/rust#115141)
 - [Enable CMPXCHG16B, SSE3, SAHF/LAHF and 128-bit Atomics in tier 1 Windows]
  (rust-lang/rust#120820)
- [Add `wasm32-wasip1` tier 2 (without host tools) target]
  (rust-lang/rust#120468)
- [Add `wasm32-wasip2` tier 3 target]
  (rust-lang/rust#119616)
- [Rename `wasm32-wasi-preview1-threads` to `wasm32-wasip1-threads`]
  (rust-lang/rust#122170)
- [Add `arm64ec-pc-windows-msvc` tier 3 target]
  (rust-lang/rust#119199)
- [Add `armv8r-none-eabihf` tier 3 target for the Cortex-R52]
  (rust-lang/rust#110482)
- [Add `loongarch64-unknown-linux-musl` tier 3 target]
  (rust-lang/rust#121832)

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

Libraries
---------

- [Bump Unicode to version 15.1.0, regenerate tables]
  (rust-lang/rust#120777)
- [Make align_offset, align_to well-behaved in all cases]
  (rust-lang/rust#121201)
- [PartialEq, PartialOrd: document expectations for transitive chains]
  (rust-lang/rust#115386)
- [Optimize away poison guards when std is built with panic=abort]
  (rust-lang/rust#100603)
- [Replace pthread `RwLock` with custom implementation]
  (rust-lang/rust#110211)
- [Implement unwind safety for Condvar on all platforms]
  (rust-lang/rust#121768)
- [Add ASCII fast-path for `char::is_grapheme_extended`]
  (rust-lang/rust#121138)

Stabilized APIs
---------------

- [`impl Read for &Stdin`]
  (https://doc.rust-lang.org/stable/std/io/struct.Stdin.html#impl-Read-for-%26Stdin)
- [Accept non `'static` lifetimes for several `std::error::Error`
  related implementations] (rust-lang/rust#113833)
- [Make `impl<Fd: AsFd>` impl take `?Sized`]
  (rust-lang/rust#114655)
- [`impl From<TryReserveError> for io::Error`]
  (https://doc.rust-lang.org/stable/std/io/struct.Error.html#impl-From%3CTryReserveError%3E-for-Error)

These APIs are now stable in const contexts:

- [`Barrier::new()`]
  (https://doc.rust-lang.org/stable/std/sync/struct.Barrier.html#method.new)

Cargo
-----

- [Stabilize lockfile v4](rust-lang/cargo#12852)
- [Respect `rust-version` when generating lockfile]
  (rust-lang/cargo#12861)
- [Control `--charset` via auto-detecting config value]
  (rust-lang/cargo#13337)
- [Support `target.<triple>.rustdocflags` officially]
  (rust-lang/cargo#13197)
- [Stabilize global cache data tracking]
  (rust-lang/cargo#13492)

Misc
----

- [rustdoc: add `--test-builder-wrapper` arg to support wrappers
  such as RUSTC_WRAPPER when building doctests]
  (rust-lang/rust#114651)

Compatibility Notes
-------------------

- [Many unsafe precondition checks now run for user code with debug
  assertions enabled] (rust-lang/rust#120594)
  This change helps users catch undefined behavior in their code,
  though the details of how much is checked are generally not
  stable.
- [riscv only supports split_debuginfo=off for now]
  (rust-lang/rust#120518)
- [Consistently check bounds on hidden types of `impl Trait`]
  (rust-lang/rust#121679)
- [Change equality of higher ranked types to not rely on subtyping]
  (rust-lang/rust#118247)
- [When called, additionally check bounds on normalized function return type]
  (rust-lang/rust#118882)
- [Expand coverage for `arithmetic_overflow` lint]
  (rust-lang/rust#119432)

Internal Changes
----------------

These changes do not affect any public interfaces of Rust, but they represent
significant improvements to the performance or internals of rustc and related
tools.

- [Update to LLVM 18](rust-lang/rust#120055)
- [Build `rustc` with 1CGU on `x86_64-pc-windows-msvc`]
  (rust-lang/rust#112267)
- [Build `rustc` with 1CGU on `x86_64-apple-darwin`]
  (rust-lang/rust#112268)
- [Introduce `run-make` V2 infrastructure, a `run_make_support`
  library and port over 2 tests as example]
  (rust-lang/rust#113026)
- [Windows: Implement condvar, mutex and rwlock using futex]
  (rust-lang/rust#121956)
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. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. 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.

None yet