-
Notifications
You must be signed in to change notification settings - Fork 29
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
Add container_of #21
Add container_of #21
Conversation
Thank you for your contribution! :) |
I don't think that this is possible since in the macro we do not know the type of the field. However I am open to suggestions! |
You could add this semi-ugly hack.
|
It's probably wise to add compile-fail tests as well |
Yeah, I heard doctests can do that? We don't have any compile-fail tests yet, that's on my to-do list, so I won't block this PR on that. Opened an issue instead: #23 |
@Amanieu could you rebase onto master so that we can see what Miri thinks of this? The more I think about this, the less Stacked Borrows likes this. You cannot use a raw pointer created for a field to access anything but that field. Creating a raw pointer from a reference "remembers" which "permissions" this reference had, and it only has permission to access the stuff it points to (its "span", defined by |
This allows name paths with :: to be passed in.
I ran Miri locally and it seems happy. |
Right, but the test is also not using the resulting pointer. Something like this is likely UB according to Stacked Borrows: struct Pair { f1: u16, f2: u16 };
let p = Pair { f1: 2, f2: 3 };
let c = container_of!(&p.f1, Pair, f1);
let _val = c.f2; |
You're right, this does indeed cause a Miri failure:
I'm not really sure what we can do about this. In C this isn't an issue because a pointer anywhere in the middle of an object (i.e an allocation) still allows you to access any byte in that object as long as you make the appropriate casts to satisfy TBAA. |
This is indeed somewhat of a unique Rust issue with its rules for references having a "span" and only being useful inside that span. Though C also has lots of rules restricting address arithmetic to work only within one "subobject", so I am not sure I agree with you that this is okay in C. I would agree though that everyone does it in C. ;) On the other hand, I think this principle is very fundamentally necessary to make Stacked Borrows any use. If uniqueness of mutable references could be legitimately violated by using arithmetic from a "neighboring" field, we might as well not bother with |
I think it might be possible to make something workable along the lines of: a reference to a field has weak rights to access the whole object, but those rights are revoked when a mutable reference is performed on one of the other fields. |
Yeah, maybe -- the referenced issue would be the place to discuss that. |
@Amanieu could you factor the macro kind changes ( |
Done |
Interestingly, if I add #[test]
fn miri() {
struct Pair {
f1: u16,
f2: u16,
};
unsafe {
let p = Pair { f1: 2, f2: 3 };
&p.f2 as *const _;
let c = container_of!(&p.f1, Pair, f1);
let _val = (*c).f2;
}
} |
Yeah that's because Miri currently makes no attempt to track raw pointers. But to match what LLVM does, we will have to do more precise tracking eventually, and then this will not work any more. It will still work if you do enough int-ptr-casts to kill provenance. At that point, what LLVM does is just entirely unknown. |
This thing does a lot! It creates a raw pointer, grants access to all raw pointers to access this memory, and then the pointer is thrown away. |
Would obtaining the field pointer using #[test]
fn miri() {
struct Pair {
f1: u16,
f2: u16,
};
unsafe {
let p = Pair { f1: 2, f2: 3 };
let f = (&p as *const _ as *const u8).add(offset_of!(Pair, f1)) as *const u16;
let c = container_of!(f, Pair, f1);
let _val = (*c).f2;
}
} |
The key is to get the pointer provenance right: the pointer to the entire struct (returned by Raw pointer operations to not affect the permissions a pointer has, so the moment you are casting a reference to a raw pointer, you are deciding what is allowed to be done with all raw pointers ever created from this one. In your example, I think |
@RalfJung What do we need in order to close/merge this? |
I'd say let's close this PR due to inactivity. I opened an issue for this at rust-lang/unsafe-code-guidelines#243 for the general problem. I am not sure if @Amanieu still considers the macro useful given these heavy limitations. @Amanieu, I am okay with having such a macro in principle, so feel free to reopen if you still think the macro is useful. But the docs need to be very clear about the fact that provenance (aka the aliasing model) is a real problem here -- and without rust-lang/rust#73394, it is basically impossible to use the macro correctly. |
I am actively using this macro in My thought was that we could modify (&parent as *const _ as *const u8).add(offset_of!($ParentType, $field)) as *const $FieldType This should create a pointer with the proper provenance which can be used with |
If you do |
Or maybe you were not talking about Line 72 in 9520967
but some other |
This adds
container_of
, an unsafe macro which allows obtaining a raw pointer to the containing struct from a pointer to one of its fields.I also fixed the other macros to take in a
path
instead of att
for the struct name, which allows complex path such asmy_mod::Foo
.