-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Tracking issue for safe_packed_borrows
compatibility lint
#46043
Comments
safe_packed_borrow
compatibility lintsafe_packed_borrows
compatibility lint
* Remove all `#[repr(packed)]` on the IMAGE structs due to a misunderstanding of the Windows.h struct definitions: Only the old 16-bit headers want 2 byte packing, all the others want 4 byte packing. However all of these structs end up already aligned anyway. Safeguard their implementation by asserting their sizes. Fixes rust-lang/rust#46043 * Explicitly specify the raw pointer type when casting from reference and calling a method on it. Fixes rust-lang/rust#46906
* Remove all `#[repr(packed)]` on the IMAGE structs due to a misunderstanding of the Windows.h struct definitions: Only the old 16-bit headers want 2 byte packing, all the others want 4 byte packing. However all of these structs end up already aligned anyway. Safeguard their implementation by asserting their sizes. Fixes rust-lang/rust#46043 * Explicitly specify the raw pointer type when casting from reference and calling a method on it. Fixes rust-lang/rust#46906
rust-lang/rust#46043 makes accessing members of packed structs unsafe. For MMIO structs, `repr(C)` alone should be sufficient as the layout by definition has no holes.
My |
See rust-lang/rust#46043 Signed-off-by: Gabriel Smith <[email protected]>
What's the time-line for turning this warning a hard-error on nightly, and moving towards making it a hard error on stable ? It has been a warning on nightly for a couple of releases already. |
@luser println!("{}", {self.packed_field}); This should work if they are However, I am worried many people do not realize what this unsafety really is about, and just add an unsafe block to silence the warning. I just read over the commit by @yodaldevoid that is linked above because they mentioned this issue (yodaldevoid/mkl26@c1c1d5c), and it contains code like (comment is mine) impl<'a> Adc<'a> {
pub fn is_conv_done(&mut self) -> bool {
// self.reg has type AdcRegs which is packed; then we call RW::read(&self, u32)
unsafe { self.reg.sc1a.read().get_bit(7) }
}
} So, this code is UB if these fields are actually unaligned. @yodaldevoid, it would be interesting to learn why you thought that passing an unaligned reference to |
@RalfJung the current warning message is pretty bad: #[repr(packed)]
struct A {
x: u8,
y: u64
}
fn main() {
let a = A { x: 1, y: 2 };
let _ = &a.y;
} outputs: warning: borrow of packed field requires unsafe function or block (error E0133)
--> src/main.rs:9:13
|
9 | let _ = &a.y;
| ^^^^
|
= note: #[warn(safe_packed_borrows)] on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> It says that taking a reference requires an unsafe block, which encourages users to do just that. Open an warning: borrow of packed field is unsafe (error E0133)
--> src/main.rs:9:13
|
9 | let _ = &a.y;
| ^^^^
|
= note: #[warn(safe_packed_borrows)] on by default
= warning: fields of packed structs might be misaligned: dereferencing a misaligned reference is undefined behavior.
= note: this is warning will turn into a hard error in the next Rust 1.2x.0 release. Taking references to packed struct fields allows undefined behavior to happen in safe Rust. Fix this with great care - for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043> It is also "only" a warning, which encourage people that numbly pursue clean builds to just want to "silence it" without digging deeper. Also some users might conclude that, if this was important, it would be a hard error, saying that it will become an error at some undetermined point in the future doesn't really convey much seriousness. |
Yeah that probably doesn't help. However, I also feel this violates the usual pattern in Rust that you should not make unsafe operations accidentally. The only other non-function-call operations we have that are unsafe involve dereferencing a raw pointer, and everyone kind-of knows why that is unsafe (though they may forget about alignment). That the mere act of taking a reference can be UB will probably be surprising to many. If they are doing this e.g. in an I think long-term we want to have an operation (that @arielb1 has proposed several times I think) that directly creates a raw pointer to a field, and we want to make taking a reference to a packed field outright illegal. People should use the raw pointer operations instead. However, I don't even know how the syntax for that should look like. |
Honestly, I would be fine with making taking references to fields of packed structs illegal at first. For FFI this is not something that's really needed. I don't know how that would interact with
Sure and I agree. As you mention, this is surprising and people can slip which is why we need to be more proactive here. |
Given that this has been possible on stable for several years, I don't think that's realistic. Though maybe we can make it illegal unless it is immediately followed by |
That would be an improvement.
Turning this warning into an error is a breaking change, and breaking changes that fix soundness bugs are "allowed". What complicates this case is that the code might actually be working fine in the platforms that the users are actually using, and that without an escape hatch users don't have a "trivally-easy" way to work around this. |
We provide alignment annotations to LLVM irrespective of the platform. So, even on platforms where all loads are allowed to be unaligned, this kind of code only "happens to work" in the sense that the compiler was not "smart enough" to exploit the UB. But yes, what I meant is that at the least, we need an escape hatch. We can't just make it entirely impossible to write such code. But before we talk about a migration strategy, I think we should have some idea of what the goal is. Do we consider There is another case where we'd like people to use such a raw-ptr operation: Imagine we have a (One reason we want it to be insta-UB is to allow more layout optimizations: Something like |
In the |
I just received this warning in
All structs defined by |
I reported a minimal example as #50826
Calm down, it's just a bug. That's why we have these warning cycles. |
For the record, declaring the borrowing of packed structure fields an unsafe operation makes Rust warn about getting the address of a packed structure field (like Some may already conclude this from the previous comments, but this is the problem I'm currently facing. |
Can't we just add an intrinsic that gives you a pointer to a value without going through a reference? Once we have that, if we really need sugar for this, we can add this later. |
@retep998 certainly, long-term the lint should be entirely independent of whether there is an unsafe block or not. Shorter-term... I suppose we could already add that lint and make it allow-by-default, would that work? |
An allow-by-default lint is perfectly fine for now. Even a clippy lint would be fine. |
Lint is implemented in #72270. |
add a lint against references to packed fields Creating a reference to an insufficiently aligned packed field is UB and should be disallowed, both inside and outside of `unsafe` blocks. However, currently there is no stable alternative (rust-lang#64490) so all we do right now is have a future incompatibility warning when doing this outside `unsafe` (rust-lang#46043). This adds an allow-by-default lint. @retep998 suggested this can help early adopters avoid issues. It also means we can then do a crater run where this is deny-by-default as suggested by @joshtriplett. I guess the main thing to bikeshed is the lint name. I am not particularly happy with "packed_references" as it sounds like the packed field has reference type. I chose this because it is similar to "safe_packed_borrows". What about "reference_to_packed" or "unaligned_reference" or so?
Both read directly into an &mut [libc::epoll_event]. That struct is packed, but it can be used safely if values are copied out and not borrowed (see rust-lang/rust#46043 for details).
So, this diagnostic notices creating a reference/pointer to a misaligned field. But isn't initializing a misaligned field also UB? #[repr(packed(2))]
struct Foo(u16, String);
let f = Foo(3, "hello!".to_owned()); seems to be accepted without complaint, and at least on x86_64, it seems to do an unaligned write. |
No, that is not UB. Rust can easily see that you're assigning a value to a misaligned location and tell LLVM to generate the correct code for it. It is only the creation of references to misaligned locations that is bad because the reference does not preserve any alignment information. |
Hi, I'm encountering this from bindgen generated files. I've given the docs to bindgen a look and don't see a good way to modify the output. Is there a recommended solution for this case? |
This is undefined behaviour: rust-lang/rust#46043 Fixes: cloud-hypervisor#70 Signed-off-by: Rob Bradford <[email protected]>
This is undefined behaviour: rust-lang/rust#46043 Fixes: #70 Signed-off-by: Rob Bradford <[email protected]>
This warning seems to be too strict for fields in a #[repr(packed(2))]
struct Foo {
x: u16,
}
fn main() {
// Warning shown here, but x is guaranteed to be aligned!
let _ = &Foo{x: 0}.x;
} This is based on my reading of the Rust reference:
The current safety rule is "only fields with align = 1 are safe to reference in a packed struct". |
I am not very familiar with #[repr(packed(2))]
struct Foo {
p : u8,
x: u16,
} The relevant bit of code would be this one:
Currently, only types with layout.align.abi.bytes() == 1 are excluded; I guess this should instead check the packed(N) and exclude layout.align.abi.bytes() <= N .
|
In that case, I've not sent in any rustc changes and am interested in contributing - could I send a PR fixing the warning? |
Yes you definitely could, that would be amazing. ❤️ Please make sure to also add some test cases that cover both cases where the lint absolutely must still fire, and cases where it should not fire any more. Probably best to add the tests for the If you have any questions, feel free to ask! I might not have a lot of time, but on our Zulip you'll find lots of helpful people. :) |
I have implemented that with #83605. |
What is this warning about
Fields of structs with the
#[repr(packed)]
attribute might be unaligned. Even if a field has a valid offset (e.g. 0) within the struct, the struct itself might be unaligned.On the other hand, safe references (
&T
and&mut T
) must always have valid alignment (even when they are just created) - not only will dereferencing these references cause segfaults on architectures such as Sparc, but the compiler is allowed to optimize based on that assumption (e.g. in a future version of rustc, it might use the "free" alignment bits to represent enum discriminants), so even just creating an unaligned reference is Undefined Behavior and might cause the program to behave in unexpected ways.Therefore, borrowing a field in the interior of a packed structure with alignment other than 1 is unsafe - if the reference is not known to be aligned, the borrow must be done to an unsafe pointer.
Note that borrowing a field with alignment 1 is always safe.
For example, consider the common
struct Unaligned
:That struct can be placed inside a bigger struct at an unaligned offset:
In that case, a borrow of a field of the struct would be Undefined Behavior (UB), and therefore is made unsafe:
This used to be left unchecked by the compiler - issue #27060.
How to fix this warning/error
The most common ways to fix this warnings are:
#[repr(packed)]
attribute if it is not actually needed. A few crates had unnecessary#[repr(packed)]
annotations - for example, tendril.In some cases, it might be necessary to borrow the fields as raw pointers and use
read_unaligned
/write_unaligned
to access them, but I haven't encountered such a case.Derives
One annoying case where this problem can appear is if a packed struct has builtin derives, e.g.
Which essentially expands to this:
If your struct already derives
Copy
and has no generics, the compiler will generate code that copies the fields to locals and will therefore work. Otherwise, you'll have to write thederive
implementations manually.For example, you might want to add a
T: Copy
bound and copy things out:The text was updated successfully, but these errors were encountered: