-
Notifications
You must be signed in to change notification settings - Fork 58
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
Is rustc guaranteed to reject code which "turns a pointer into raw bytes" in const code? #401
Comments
I have been cautioned before about treating pointers as "just funny integers", because actual integers don't carry provenance. I have been told that the most likely path for Rust will eventually involve any plain transmutation of integer data to pointer data will end up producing an invalid pointer. I don't want to overstate things, I don't think this is a settled matter, but I suspect these impls would be ill advised to add right now, even if they're only used at runtime. |
I think you've misinterpreted the question (or at least I don't see the connection). They're asking if they can rely on a const-eval failure here, which I believe the answer from the |
The docs on these types didn't make it clear whether they are expected to be round-tripping, maybe it was assumed that this is the case? As far as I know, both @thomcc I think this is relevant not for the question that was asked but the claim that "If it's the former, then we can soundly emit the trait impls proposed in that PR, but if not, those impls would be unsound", because I'm not sure whether it would be sound even in the former case. Or at least, the impls are sound but foot-gunny, since most code that would use the impls is not sound (although one would have to write additional downstream |
Yeah so my question is roughly: "As we all know, it is sound to convert IIUC, @Lokathor is questioning the premise of the question and pointing out that it may not even be sound to do a pointer-to-bytes or bytes-to-pointer conversion at runtime, let alone at const eval time. IIUC, @thomcc is saying that there is no guarantee that the compiler will catch this mistake at const eval time. IIUC, @digama0 is saying that while it might be sound to do a pointer-to bytes or bytes-to-pointer conversion at runtime in the general case, it would only be sound to actually dereference such a pointer under certain cases. As a result, even if the trait impls I'm proposing are technically sound, they likely constitute a footgun. Do I understand everyone's points correctly? |
At const eval time, I don't think there is any danger of immediate UB, although there is certainly the danger of a compile failure. The compiler is allowed to evaluate ptr-to-int conversions, it just can't in most cases. In particular, if the pointer is a |
Okay so if I'm reading this correctly, then we could be in one of two possible situations. Is that right? Situation 1: Any pointer-to-int or int-to-pointer conversion is valid (although of course dereferencing such a pointer might be invalid). If this is the case, then any such conversion at const eval time is either unsupported (const eval will fail) or is a sound operation at const eval time. There's no possibility to introduce UB at const eval time just by enabling such a conversion. Situation 2: Some pointer-to-int or int-to-pointer conversions are unsound/instant UB. If this is the case, then performing such a conversion is UB at both runtime and const eval time. My take-away from this is that either we're in Situation 1, and zerocopy can support these conversions without worrying about it having different soundness at runtime vs const eval time, or we're in Sitaution 2, and it would be unsound for zerocopy to support these conversions at all, even at runtime. |
AFAIK we have not gone through the relevant stabilization process to determine what the situation is, but I would put a good probability on situation 1. I don't know any in flight proposals that would require making ptr-to-int or int-to-ptr conversions immediate UB (except for some early versions of the strict provenance model). |
Unless I have misunderstood the motivating context of this question, I think this has become sidetracked into a question about UB and the validity or semantics of pointer-integer conversions. I don't see any question about UB in the original question, and all I see in motivation is a concern about people using these APIs to I guess smuggle a pointer through a usize. I don't understand why specifically const eval detecting this is a great help... But anyway. I am not sure this is a Rust semantics question or a question about UB detection in const eval. All that being said, I also do not think that we guarantee this const evaluation error. I would certainly not rely on it for soundness. But in the current interpreter implementation, I would not classify this detection as "best-effort". For sure the UB detection is best-effort, but this is not the same. |
Also cc @oli-obk (since this is an interpreter question and by asking a question on this repo you have already summoned the rest of the Miri team) |
I think that the implicit (and maybe wrong) assumption in my original question has to do with this error that was encountered while testing the PR:
I assumed that this error represented the compiler complaining about UB, and so if the compiler were to fail to generate an error for this case or a similar one, it would mean UB slipping by. From the discussion, however, it seems like maybe the error doesn't represent UB, but just represents the compiler saying "hey, I can't do that at const eval time", and if the error were to go away, it would just mean that the compiler can now do a new thing, so all is fine. If it's the former case, then it would be unsound for zerocopy to produce an API (namely the |
Yeah, thanks for substantiating my guess. At const eval time, a pointer is not an address. It is an allocation ID and an offset into the allocation. Allocations don't have a base address, memory is just a |
For const eval we can't expose any addresses as those won't be known until the dynamic linker has loaded the program. Instead we have to keep pointers symbolic values whose real value gets filled in by the dynamic linker through relocations. These relocations can only represent a very limited set of values, so we currently don't allow any operations on the address of pointers other than pointer offsets to ensure they stay representable. |
There could be a theoretical const evaluator implementation that preserves provenance in ptr2int casts, and thus makes your program compile and allow round tripping with int2ptr at compile and run-time. If (and that's a very unlikely I would recommend not making your conversion function |
This is not true for some notions of "sound" and depending on what exactly the conversion does. So the reason the discussion got side-tracked is that it uncovered some flawed underlying assumptions. Note that Miri does not currently correctly detect the flaw in your reasoning here, that is issue rust-lang/miri#2182. That issue also contains examples of code that performs such conversions and causes UB: use std::mem;
fn main() {
let ptrs = [&42];
let ints: [usize; 1] = unsafe { mem::transmute(ptrs) };
let ptr = ints.as_ptr().cast::<&i32>();
let _val = unsafe { *ptr.read() }; //~ERROR
} This uses If you replace Regarding your original question: I don't think we are ready to make any guarantees regarding const-eval definitely triggering an error in some situation. |
It might help for me to give a bit more context. The traits in question are We also provide the The PR that started this discussion simply implements
My question is: Is it sound for me to implement those traits for The reason I asked about const eval in particular is that I was worried that, even though the conversions may be sound at runtime, maybe they aren't sound at const eval time? The consensus as I understand it in this discussion is that the error I got wasn't telling me that the conversion was UB at const eval time, but rather just unsupported, and that if it becomes supported at some point in the future, it will not be UB. However, the discussion also seems to call into question my assumption that the conversions in question are sound even at runtime. Note that I do not support dereferencing raw pointers. IIUC, the trait impls are only unsound if creating invalid raw pointers is in and of itself UB even if those pointers are never dereferenced. So my new question is: Am I guaranteed that converting between raw pointers and byte arrays/ |
So the question you need to answer is if people expect to be able to turn a value into bytes and then back "losslessly", and use that new value exactly like it's the old value. If people expect this then you shouldn't make the impls for pointers. The conversion itself is fine, but it is not lossless: the output is always an invalid pointer. Turning a valid pointer into an array of |
Yeah, this is my new question: I know this is roughly true today (I say roughly because it's technically not well-defined, rust doesn't have a memory model, etc etc), but am I safe to assume that this will always be true, or is there a reasonable chance that some future clarification of the memory model would make such a conversion instant UB? |
I think you're safe from instant ub even into the future, yes. Still a footgun you probably don't want to add though. |
This is, unfortunately, exactly the question that stabilization is meant to answer. So until stabilization we really can't say for sure that this is a safe assumption now and in the future. So far it's looking like it will be true, but I would rather you not read more into this than a weather prediction. |
One extra wrinkle to note is that the Thus, transmuting pointers instead of doing an So, as stated, it comes down to a question of managing expectations. The important part to me that makes me say no is that any safe wrapper around pointers would obviously be unsound to implement But technically speaking, it's indeed not guaranteed yet that transmuting from pointer to integer is actually a sound operation, and earlier provenance models made doing so UB. It's unlikely that it will be UB given current knowledge, but still possible. Weather forecast for next week rather than next month. Footnotes
|
Thanks for the thorough writeup, @CAD97! A lot to think about there. There's also a discussion on the zerocopy repo if anyone wants to participate over there and avoid polluting the thread here. |
Indeed, I don't think we can commit to this yet.
This is technically correct. However, I assume you would consider a Of course you could be defining these traits to be about the conversion to/from arrays of |
So I think the original const-eval question is answered and the other discussion hopefully clarified (and we already have issues tracking those provenance transmutation questions). What remains for me is a question to you: how useful would it be to have these |
In google/zerocopy#171 (comment), we're adding the ability to safely convert between byte slices/arrays and raw pointers. Currently, we have a test that is successfully failing if we try to perform this operation in a const context. What I want to know is: Is it guaranteed that code that performs any such conversion in a const context will always be rejected, or is this a best-effort analysis? If it's the former, then we can soundly emit the trait impls proposed in that PR, but if not, those impls would be unsound.
The text was updated successfully, but these errors were encountered: