Skip to content
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

Missed UB around assignments and function calls when using custom MIR #2927

Closed
RalfJung opened this issue Jun 14, 2023 · 7 comments · Fixed by rust-lang/rust#113569
Closed
Labels
I-misses-UB Impact: makes Miri miss UB, i.e., a false negative (with default settings)

Comments

@RalfJung
Copy link
Member

There are some open questions around assignments and function calls:

When writing custom MIR, Miri currently definitely misses some UB here due to ignoring those questions. For function calls, we have examples of that. For assignments, I am not entirely sure what the current status is -- if I understood @bjorn3 correctly, we do use memcpy for some kinds of assignments, so it should be possible to write MIR that is accepted by Miri but UB in LLVM? I hope this requires custom MIR, or can something like that be written in surface Rust?

@RalfJung RalfJung added the I-misses-UB Impact: makes Miri miss UB, i.e., a false negative (with default settings) label Jun 14, 2023
@bjorn3
Copy link
Member

bjorn3 commented Jun 14, 2023

I hope this requires custom MIR, or can something like that be written in surface Rust?

MIR building currently defensively emits a copy basically everywhere. MIR optimizations may allow observing move operands though depending on which ones have been enabled right now. And I hope that in the future we can stop emitting copies as aggresively during MIR building as it reduces effectivity of MIR optimizations, hurts perf for unoptimized builds and even somewhat (and even a lot in some cases) for fully optimized builds.

memcpy is used for all assignments of non-primitive types. Function returns work either using registers or a return value pointer which allows the callee to directly write to the return place of the caller's frame. Copy function arguments are copied to a temporary if they are by-ref. Move function arguments directly pass a pointer to the place of the move operand.

@RalfJung
Copy link
Member Author

MIR optimizations may allow observing move operands though depending on which ones have been enabled right now.

How does an operand being move affect codegen / MIR optimizations (outside of fn calls)?

memcpy is used for all assignments of non-primitive types.

What exactly is "primitive" here? Is this about having an Aggregate ABI?

@bjorn3
Copy link
Member

bjorn3 commented Jun 14, 2023

How does an operand being move affect codegen / MIR optimizations (outside of fn calls)?

Codegen doesn't distinguish between copy and move in assignments. Only for function calls is it special cased.

What exactly is "primitive" here? Is this about having an Aggregate ABI?

Yes, I believe so.

@RalfJung
Copy link
Member Author

Yes, I believe so.

I guess I could implement the reborrow-based approach outlined here and only use it for aggregate ABI assignments to make sure we check what codegen needs, but that feels very arbitrary.

Elsewhere you said once that this affects only some rvalue expressions -- is that based on the observation that e.g. BinOp always returns a Scalar/ScalarPair (i.e., a primitive)?

Do you know where in codegen this decision is made? I found this which looks promising. The decision whether to use OperandValue::Ref is also made here, but that seems to be about function calls so I guess it is different.

@bjorn3
Copy link
Member

bjorn3 commented Jun 15, 2023

I found this which looks promising.

It is around https://github.com/rust-lang/rust/blob/4996b56ba9647d149265d03dcbd9ab837af3a1bb/compiler/rustc_codegen_ssa/src/mir/mod.rs#L226-L251 to determine if it could be stored as SSA value in the first place or needs to be on the stack, and then https://github.com/rust-lang/rust/blob/4996b56ba9647d149265d03dcbd9ab837af3a1bb/compiler/rustc_codegen_ssa/src/mir/statement.rs#L16-L39 handles the actual assignment depending on how it is stored.

For cg_clif the logic is simply https://github.com/bjorn3/rustc_codegen_cranelift/blob/45781e107c37524386478b02082700f09bd10d78/src/abi/mod.rs#L196-L204 for locals where is_ssa will be false for Abi::Aggregate locals. https://github.com/bjorn3/rustc_codegen_cranelift/blob/45781e107c37524386478b02082700f09bd10d78/src/abi/mod.rs#L295-L319 for arguments and finally https://github.com/bjorn3/rustc_codegen_cranelift/blob/45781e107c37524386478b02082700f09bd10d78/src/abi/returning.rs#L10 for the return value _0.

The decision whether to use OperandValue::Ref is also made here, but that seems to be about function calls so I guess it is different.

Yeah, that is for function calls.

@RalfJung
Copy link
Member Author

RalfJung commented Jul 9, 2023

rust-lang/rust#113441 should fix the situation with assignments.

@RalfJung
Copy link
Member Author

@bjorn3 if you could take a look at the tests added in rust-lang/rust#113569 and whether they reflect all the UB we need for Move argument passing, that would be great. :)

RalfJung pushed a commit to RalfJung/miri that referenced this issue Jul 12, 2023
miri: protect Move() function arguments during the call

This gives `Move` operands a meaning specific to function calls:
- for the duration of the call, the place the operand comes from is protected, making all read and write accesses insta-UB.
- the contents of that place are reset to `Uninit`, so looking at them again after the function returns, we cannot observe their contents

Turns out we can replace the existing "retag return place" hack with the exact same sort of protection on the return place, which is nicely symmetric.

Fixes rust-lang/rust#112564
Fixes rust-lang#2927

This starts with a Miri rustc-push, since we'd otherwise conflict with a PR that recently landed in Miri.
(The "miri tree borrows" commit is an unrelated cleanup I noticed while doing the PR. I can remove it if you prefer.)
r? `@oli-obk`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
I-misses-UB Impact: makes Miri miss UB, i.e., a false negative (with default settings)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants