-
-
Notifications
You must be signed in to change notification settings - Fork 34
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
Can bypass repr(packed)
checking by hiding it in a proc-macro-attribute
#32
Comments
I don't think this issue can be avoided without compiler support. But, I think this should probably be mentioned in the documents. |
Refs: rust-lang/reference#578 |
rust-lang/reference#578 seems to mostly be about execution order for side-effects, rust-lang/rust#63336 is more relevant as that is about the order in which multiple attributes affect the AST of a single item (but should probably be opened as a new reference issue). |
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 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 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.
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 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 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.
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.
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.
34: Ensure that #[repr(packed)] cannot be used on #[pin_projectable] structs r=taiki-e a=Aaron1011 As described in issue #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. Note: I've removed the tests for `#[repr(packed)]` being denied on enums - `#[repr(packed)]` isn't even allowed on enums in the first place, so there's no need to do anything. Co-authored-by: Aaron Hill <aa1ronham@gmail.com> Co-authored-by: Taiki Endo <te316e89@gmail.com>
This should now be fixed in |
Yes, thanks for the fix this issue @Aaron1011. |
Given a proc-macro crate like this:
Using it along with
pin-project
in a project can cause a value to be pinned in one location and dropped in another:output:
(it appears proc-macro-attribute application order is not defined, so not guaranteed to reproduce, but this seems like the sane order for rustc to apply them in so it's likely to continue working).
The text was updated successfully, but these errors were encountered: