forked from taiki-e/pin-project
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Ensure that #[repr(packed)] cannot be used on #[pin_projectable] structs
As described in issue taiki-e#32, it's possible to bypass the #[repr(packed)] check via the use of an additional procedural macro. However, we need to be able to forbid using #[repr(packed) in order to make #[pin_projectable] sound To enforce this, we can make use of the fact that taking references to the fields of a #[repr(packed)] struct is unsafe. Given a #[repr(packed)] struct Foo, we can generate code like this: ```rust fn check_Foo(val: Foo) { &val.field1; &val.field2; ... &val.fieldn; } ``` If `Foo` turns out to be #[repr(packed)], the compiler will generate an error for us, since none of the field references are in an `unsafe` block. Unfortunately, this check requires that at least one of the struct's fields have an alignment greater than 1. If the struct is composed entirely of fields of alignment 1 (e.g. 'u8'), it will be safe to take a reference to any of the fields, preventing an error from being emiited. However, I believe that pin projecting such a struct is actually sound. If all fields have an alignment 1, #[repr(packed)] is effectively a no-op, as struct will already have no padding. I've added a test to verify that the compiler does not copy the fields of such a struct when it is dropped.
- Loading branch information
Showing
7 changed files
with
179 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#![warn(unsafe_code)] | ||
#![warn(rust_2018_idioms)] | ||
#![allow(dead_code)] | ||
|
||
use std::cell::Cell; | ||
use std::thread; | ||
|
||
// Ensure that the compiler doesn't copy the fields | ||
// of #[repr(packed)] types during drop, if the field has alignment 1 | ||
// (that is, any reference to the field is guaranteed to have proper alignment) | ||
// We are currently unable to statically prevent the usage of #[pin_projectable] | ||
// on #[repr(packed)] types composed entirely of fields of alignment 1. | ||
// This shouldn't lead to undefined behavior, as long as the compiler doesn't | ||
// try to move the field anyway during drop. | ||
// | ||
// This tests validates that the compiler is doing what we expect. | ||
#[test] | ||
fn weird_repr_packed() { | ||
// We keep track of the field address during | ||
// drop using a thread local, to avoid changing | ||
// the layout of our #[repr(packed)] type | ||
thread_local! { | ||
static FIELD_ADDR: Cell<usize> = Cell::new(0); | ||
} | ||
|
||
#[repr(packed)] | ||
struct Foo { | ||
field: u8, | ||
} | ||
|
||
impl Drop for Foo { | ||
fn drop(&mut self) { | ||
FIELD_ADDR.with(|f| { | ||
f.set(&self.field as *const u8 as usize); | ||
}) | ||
} | ||
} | ||
|
||
let field_addr = { | ||
// We let this field drop by going out of scope, | ||
// rather than explicitly calling drop(foo). | ||
// Calling drop(foo) causes 'foo' to be moved | ||
// into the 'drop' function, resulinting a different | ||
// address | ||
let foo = Foo { field: 27 }; | ||
let field_addr = &foo.field as *const u8 as usize; | ||
field_addr | ||
}; | ||
assert_eq!(field_addr, FIELD_ADDR.with(|f| f.get())); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// force-host | ||
// no-prefer-dynamic | ||
|
||
#![crate_type = "proc-macro"] | ||
|
||
extern crate proc_macro; | ||
|
||
use proc_macro::TokenStream; | ||
|
||
#[proc_macro_attribute] | ||
pub fn hidden_repr(attr: TokenStream, item: TokenStream) -> TokenStream { | ||
format!("#[repr({})] {}", attr, item).parse().unwrap() | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,14 @@ | ||
error: pin_projectable may not be used on #[repr(packed)] types | ||
--> $DIR/packed.rs:8:8 | ||
--> $DIR/packed.rs:9:8 | ||
| | ||
8 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type | ||
9 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type | ||
| ^^^^^^ | ||
|
||
error: pin_projectable may not be used on #[repr(packed)] types | ||
--> $DIR/packed.rs:15:8 | ||
--> $DIR/packed.rs:16:8 | ||
| | ||
15 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type | ||
16 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type | ||
| ^^^^^^ | ||
|
||
error: pin_projectable may not be used on #[repr(packed)] types | ||
--> $DIR/packed.rs:23:8 | ||
| | ||
23 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type | ||
| ^^^^^^ | ||
|
||
error: pin_projectable may not be used on #[repr(packed)] types | ||
--> $DIR/packed.rs:28:8 | ||
| | ||
28 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type | ||
| ^^^^^^ | ||
|
||
error: aborting due to 4 previous errors | ||
error: aborting due to 2 previous errors | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// compile-fail | ||
// aux-build:sneaky_macro.rs | ||
|
||
#[macro_use] | ||
extern crate sneaky_macro; | ||
|
||
use pin_project::pin_projectable; | ||
|
||
#[pin_projectable] //~ ERROR borrow of packed field is unsafe and requires unsafe function or block | ||
#[hidden_repr(packed)] | ||
struct Bar { | ||
#[pin] | ||
field: u32 | ||
} | ||
|
||
fn main() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
error: borrow of packed field is unsafe and requires unsafe function or block (error E0133) | ||
--> $DIR/packed_sneaky.rs:9:1 | ||
| | ||
9 | #[pin_projectable] //~ ERROR borrow of packed field is unsafe and requires unsafe function or block | ||
| ^^^^^^^^^^^^^^^^^^ | ||
| | ||
note: lint level defined here | ||
--> $DIR/packed_sneaky.rs:9:1 | ||
| | ||
9 | #[pin_projectable] //~ ERROR borrow of packed field is unsafe and requires unsafe function or block | ||
| ^^^^^^^^^^^^^^^^^^ | ||
= 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> | ||
= note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior | ||
|
||
error: aborting due to previous error | ||
|