Skip to content

try_cast_aligned: avoid bare int-to-ptr casts #141381

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

Merged
merged 1 commit into from
May 22, 2025

Conversation

RalfJung
Copy link
Member

This fixes a CI failure in https://github.com/rust-lang/miri-test-libstd caused by strict provenance violations in doctests added in #141222.

r? @tgross35
Cc @mathisbot

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels May 22, 2025
@mathisbot
Copy link
Contributor

mathisbot commented May 22, 2025

I totally missed that..
I think that without_provenance make the examples less readable (in particular for the NonNull) so I was trying to think of another example that would use provenance but I guess it is not easy to create a reference to an object that is for sure not properly aligned (for a larger aligment).

@RalfJung
Copy link
Member Author

it is not easy to create a reference to an object that is for sure not properly aligned (for a larger aligment).

The best way is to do something like

let x = &0i32; // guaranteed 4-aligned
let ptr = (x as *const i32).cast::<u8>().add(1); // guaranteed not 4-aligned

@RalfJung
Copy link
Member Author

RalfJung commented May 22, 2025

I think that without_provenance make the examples less readable

Maybe they do, but the original example is not representative of how we want anyone to write Rust code. int2ptr casts are extremely rarely needed. This is the only test (across all doc tests, unit tests, integration tests) in all of core, alloc, and the parts of std that Miri can run that uses them!

@mathisbot
Copy link
Contributor

mathisbot commented May 22, 2025

The best way is to do something like

let x = &0i32; // guaranteed 4-aligned
let ptr = (x as *const i32).cast::<u8>().add(1); // guaranteed not 4-aligned

it is less expressive than raw adresses that are obviously well/not aligned.
Do you think this is better than using raw int2ptr? In particular regarding your next comment:

Maybe they do, but the original example is not representative of how we want anyone to write Rust code. int2ptr casts are extremely rarely needed. This is the only test (across all doc tests, unit tests, integration tests) in all of core, alloc, and the parts of std that Miri can run that uses them!

I totally agree. According to you, how big of a problem is it ? Maybe we should totally remove such a cast to be more idiomatic?

@RalfJung
Copy link
Member Author

RalfJung commented May 22, 2025

It's a problem because it causes CI in https://github.com/rust-lang/miri-test-libstd/ to fail. We don't have a good way of allowing such casts in a single test -- and even if we did, I don't think we should, since we consider such casts to be very unidiomatic (except in special situations such as MMIO accesses to hardware registers at fixed addresses). So I think everything is better than using int2ptr casts.

If you think the examples I proposed are too unreadable, I'd encourage you to write alternative examples that also avoid int2ptr casts. Then we can see which one @tgross35 prefers. :)

@workingjubilee
Copy link
Member

it is less expressive than raw adresses that are obviously well/not aligned.

this is confusing to me. offsetting a pointer to a 4-byte object by 1 byte feels quite expressive, because it expresses it without drawing attention to irrelevant minutiae like the specific address.

@mathisbot
Copy link
Contributor

mathisbot commented May 22, 2025

What I wanted to say is that I found the without_provenance example more readable than using references and cast+add, but it is unclear to me wether without_provenance is included in the unidiomatic int2ptr casts.
If yes, then maybe we should use references, if no, then let’s keep it like you originally proposed.
To be extra clear: all your examples are very readable, Im just trying to figure out which one is the best

@mathisbot
Copy link
Contributor

this is confusing to me. offsetting a pointer to a 4-byte object by 1 byte feels quite expressive, because it expresses it without drawing attention to irrelevant minutiae like the specific address.

I think that settles it? What about something like this (directly using what @RalfJung wrote):

let x = 0;

let aligned: *const i32 = &x;
let unaligned = unsafe { aligned.byte_add(1) };

assert!(aligned.try_cast_aligned::<u32>().is_some());
assert!(unaligned.try_cast_aligned::<u32>().is_none());

I like because the pointer-creation part doesn't bloat the example (especially since NonNull::from_ref has been stabilised).

@RalfJung
Copy link
Member Author

RalfJung commented May 22, 2025 via email

@workingjubilee
Copy link
Member

let x = 0;

let aligned: *const i32 = &x;
let unaligned = unsafe { aligned.byte_add(1) };

assert!(aligned.try_cast_aligned::<u32>().is_some());
assert!(unaligned.try_cast_aligned::<u32>().is_none());

I like because the pointer-creation part doesn't bloat the example (especially since NonNull::from_ref has been stabilised).

Yeah, that seems good.

With pointers it can be easy to wander pretty far into the weeds, away from real code that people actually write, so "simplest example that reuses code people actually want to write" seems most helpful to me. The "fact" that pointers "are integers" usually only comes up when you have two of them and then try to do math on their addresses. Alone, each is mostly just its offset from an origin, because you can't actually usefully interact with their address (since it's unstable).

@tgross35
Copy link
Contributor

Ahk sorry for missing this. I think I have a slight preference for the example using real pointers rather than magic numbers since I agree that seems more realistic.

let x = 0;

let aligned: *const i32 = &x;
let unaligned = unsafe { aligned.byte_add(1) };

assert!(aligned.try_cast_aligned::<u32>().is_some());
assert!(unaligned.try_cast_aligned::<u32>().is_none());

One note to this example is it would make more sense as let aligned: *const u64 = &0; so unaligned = ptr + 1 would still be valid for a read if the alignment were correct.

@RalfJung RalfJung force-pushed the try_cast_aligned-strict-provenance branch from c2adb49 to 204d174 Compare May 22, 2025 10:31
@tgross35
Copy link
Contributor

@bors r+ rollup

@bors
Copy link
Collaborator

bors commented May 22, 2025

📌 Commit 204d174 has been approved by tgross35

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. labels May 22, 2025
@RalfJung RalfJung force-pushed the try_cast_aligned-strict-provenance branch from 204d174 to 09ae053 Compare May 22, 2025 11:32
@RalfJung
Copy link
Member Author

One note to this example is it would make more sense as let aligned: *const u64 = &0; so unaligned = ptr + 1 would still be valid for a read if the alignment were correct.

That resolves my one concern with the example, so I went for that.

The last push changes a from_ref to from_mut since we're passing it an &mut anyway.

@bors r=tgross35

@bors
Copy link
Collaborator

bors commented May 22, 2025

📌 Commit 09ae053 has been approved by tgross35

It is now in the queue for this repository.

bors added a commit to rust-lang-ci/rust that referenced this pull request May 22, 2025
…iaskrgr

Rollup of 8 pull requests

Successful merges:

 - rust-lang#141355 (ci: improve citool job db errors)
 - rust-lang#141359 (Fix `FnOnce` impl for `AsyncFn`/`AsyncFnMut` self-borrowing closures in new solver)
 - rust-lang#141362 (Normalize aliases to correct kind of error term)
 - rust-lang#141377 (Remove unnecessary `is_empty` checks)
 - rust-lang#141381 (try_cast_aligned: avoid bare int-to-ptr casts)
 - rust-lang#141382 (ci: convert distcheck to free runner)
 - rust-lang#141389 (ci: prepare aws access keys for migration)
 - rust-lang#141390 (Don't allow `poly_select` in new solver)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit db95253 into rust-lang:master May 22, 2025
6 checks passed
@rustbot rustbot added this to the 1.89.0 milestone May 22, 2025
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request May 22, 2025
Rollup merge of rust-lang#141381 - RalfJung:try_cast_aligned-strict-provenance, r=tgross35

try_cast_aligned: avoid bare int-to-ptr casts

This fixes a CI failure in https://github.com/rust-lang/miri-test-libstd caused by strict provenance violations in doctests added in rust-lang#141222.

r? `@tgross35`
Cc `@mathisbot`
@RalfJung RalfJung deleted the try_cast_aligned-strict-provenance branch May 22, 2025 18:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants