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

Detect incorrect vtable alignment during const eval #86174

Merged
merged 1 commit into from
Jun 12, 2021

Conversation

lqd
Copy link
Member

@lqd lqd commented Jun 9, 2021

This PR fixes #86132 by detecting invalid alignment values for trait objects in the interpreter, and emitting an error about this conversion failure, to avoid the ICE.

I've noticed that the error emitted at

throw_ub_format!(
"invalid vtable: \
size is bigger than largest supported object"
);
doesn't seem to be present in the const-ub tests, so I've tried adding a test that triggers both of these cases: one for the invalid size, and another for the invalid alignment that #86132 tracks (I have found different magic values triggering different Align::from_bytes errors than the "power of 2" one, if need be).

However, when doing that, I cannot for the life of me figure out the correct incantation to make these 2 errors trigger with the "it is undefined behavior to use this value" message rather than the "any use of this value will cause an error" lint.

I've tried Oli's suggestions of different values, tuples and arrays, using the transparent wrapper trick from the other tests and I was only able to trigger the regular const-ub errors about the size of the vtable, or that the drop pointer was invalid. Maybe these "type validation failed" errors happen before this part of the interpreter is reached and there just needs some magic incorrect values to bypass them, I don't know.

Since this fixes an ICE, and if the constants are indeed used, these 2 tests will turn into a hard error, I thought I'd open the PR anyways. And if @RalfJung you know of a way I could manage that (if you think that these tests are worth checking that the throw_ub_format! does indeed create const-ub errors as we expect) I'd be grateful.

For that reason, r? @RalfJung and cc @oli-obk.

@rust-highfive
Copy link
Collaborator

Some changes occured to the CTFE / Miri engine

cc @rust-lang/miri

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jun 9, 2021
@RalfJung
Copy link
Member

RalfJung commented Jun 9, 2021

However, when doing that, I cannot for the life of me figure out the correct incantation to make these 2 errors trigger with the "it is undefined behavior to use this value" message rather than the "any use of this value will cause an error" lint.

This is the error I expected -- it is a UB message coming from regular execution, not part of validation. If you can trigger this bug in validation that will likely ICE as the read_size_and_align_from_vtable call currently does not catch these UB messages...

I think what happens is that already during interning (or even earlier), we try to read the vtable and hence run into the "lint" error path. This means we never even reach validation where we would show a hard error and a different message. You could try to set RUSTC_CTFE_BACKTRACE=1 to see where in the CTFE engine the error originates; the backtrace should tell you which pass of const-evaluation is triggering this.

@lqd
Copy link
Member Author

lqd commented Jun 9, 2021

I think what happens is that already during interning (or even earlier), we try to read the vtable and hence run into the "lint" error path. This means we never even reach validation where we would show a hard error and a different message. You could try to set RUSTC_CTFE_BACKTRACE=1 to see where in the CTFE engine the error originates; the backtrace should tell you which pass of const-evaluation is triggering this.

I'm not super versed in miri traces but there's indeed a difference that seems to match your intuition:

  • the UB error message added by this PR seems triggered by what I believe I would call "regular const evaluation" ^^ (full trace here)
  • while the hard errors -- which I believe are the ones with actual UndefinedBehaviorInfos -- happen during validation (full trace here)

@RalfJung
Copy link
Member

RalfJung commented Jun 9, 2021

while the hard errors -- which I believe are the ones with actual UndefinedBehaviorInfos -- happen during validation

Their distinguishing factor is that they are UndefinedBehaviorInfo::ValidationFailure, which are raised during a second phase, after actual const-evalaution, which handles errors differently.

the UB error message added by this PR seems triggered by what I believe I would call "regular const evaluation"

Yeah, looks like something is dereferencing this reference; I assume there is an implicit reborrow happening somewhere.

I think you should totally keep these testcases, but here's an idea for another one that might trigger a ValidationFailure:

#[repr(transparent)]
struct W<T>(T);

const INVALID_VTABLE_SIZE: W<&dyn Send> =
    unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 0usize])) };

The wrapping W prevents the compiler from introducing reborrows, which avoids erroring early during evalutaion, so instead now we error late during validation. We error due to a bad drop pointer though, as you said -- which is correct, 1usize is indeed a bad drop pointer. ;)

So let's fabricate a proper drop pointer. In fact, here's how to cause an ICE since this kind of error is not handled properly 😂

#[repr(transparent)]
struct W<T>(T);

// The drop fn is checked before size/align are, so get ourselves a "sufficiently valid" drop fn
fn drop_me(_: *mut usize) {}

const INVALID_VTABLE_SIZE: W<&dyn Send> =
    unsafe { std::mem::transmute((&92u8, &(drop_me as fn(*mut usize), usize::MAX, 1usize))) };

also add tests for these 2 kinds of errors for size and alignment,
as the existing size check wasn't apparently tested
@lqd
Copy link
Member Author

lqd commented Jun 9, 2021

I think you should totally keep these testcases, but here's an idea for another one that might trigger a ValidationFailure:

#[repr(transparent)]
struct W<T>(T);

const INVALID_VTABLE_SIZE: W<&dyn Send> =
    unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 0usize])) };

The wrapping W prevents the compiler from introducing reborrows, which avoids erroring early during evalutaion, so instead now we error late during validation. We error due to a bad drop pointer though, as you said -- which is correct, 1usize is indeed a bad drop pointer. ;)

yes absolutely, this is what I was alluding to (very obscurely) in the OP: I've seen this "transparent wrapper trick" to prevent reborrows, in the ub-wide-ptr.rs test. With the test cases above triggering the lint errors, I had tried using W and it does indeed create validation failures about the drop pointer -- and this is all I was able to get. I was unfortunately expecting/hoping for a validation failure with the lint mesgs... and that seems more easily doable with the ICE you've found 😂.

I didn't end up adding a test case like this one because invalid drop pointers seemed well tested.

@lqd
Copy link
Member Author

lqd commented Jun 9, 2021

At least validation is honest about why it ICEs :)

// Avoid other errors as those do not show *where* in the value the issue lies.
Err(err) => {
err.print_backtrace();
bug!("Unexpected error during validation: {}", err);
}
}

@RalfJung
Copy link
Member

Right, so we should fix that ICE and I can give you some hints for how to do that, but independent of that I think we can first land this one here. Thanks for the patch!

@bors r+ rollup

@bors
Copy link
Contributor

bors commented Jun 10, 2021

📌 Commit d449903 has been approved by RalfJung

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 10, 2021
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this pull request Jun 11, 2021
Detect incorrect vtable alignment during const eval

This PR fixes rust-lang#86132 by detecting invalid alignment values for trait objects in the interpreter, and emitting an error about this conversion failure, to avoid the ICE.

I've noticed that the error emitted at https://github.com/rust-lang/rust/blob/a50d72158e08e02cfc051b863017bdbd2c45b637/compiler/rustc_mir/src/interpret/traits.rs#L163-L166 doesn't seem to be present in the const-ub tests, so I've tried adding a test that triggers both of these cases: one for the invalid size, and another for the invalid alignment that rust-lang#86132 tracks (I have found different magic values triggering different `Align::from_bytes` errors than the "power of 2" one, if need be).

However, when doing that, I *cannot* for the life of me figure out the correct incantation to make these 2 errors trigger with the "it is undefined behavior to use this value" message rather than the "any use of this value will cause an error" lint.

I've tried Oli's suggestions of different values, tuples and arrays, using the transparent wrapper trick from the other tests and I was only able to trigger the regular const-ub errors about the size of the vtable, or that the drop pointer was invalid. Maybe these "type validation failed" errors happen before this part of the interpreter is reached and there just needs some magic incorrect values to bypass them, I don't know.

Since this fixes an ICE, and if the constants are indeed used, these 2 tests will turn into a hard error, I thought I'd open the PR anyways. And if `@RalfJung` you know of a way I could manage that (if you think that these tests are worth checking that the `throw_ub_format!` does indeed create const-ub errors as we expect) I'd be grateful.

For that reason, r? `@RalfJung` and cc `@oli-obk.`
JohnTitor added a commit to JohnTitor/rust that referenced this pull request Jun 11, 2021
Detect incorrect vtable alignment during const eval

This PR fixes rust-lang#86132 by detecting invalid alignment values for trait objects in the interpreter, and emitting an error about this conversion failure, to avoid the ICE.

I've noticed that the error emitted at https://github.com/rust-lang/rust/blob/a50d72158e08e02cfc051b863017bdbd2c45b637/compiler/rustc_mir/src/interpret/traits.rs#L163-L166 doesn't seem to be present in the const-ub tests, so I've tried adding a test that triggers both of these cases: one for the invalid size, and another for the invalid alignment that rust-lang#86132 tracks (I have found different magic values triggering different `Align::from_bytes` errors than the "power of 2" one, if need be).

However, when doing that, I *cannot* for the life of me figure out the correct incantation to make these 2 errors trigger with the "it is undefined behavior to use this value" message rather than the "any use of this value will cause an error" lint.

I've tried Oli's suggestions of different values, tuples and arrays, using the transparent wrapper trick from the other tests and I was only able to trigger the regular const-ub errors about the size of the vtable, or that the drop pointer was invalid. Maybe these "type validation failed" errors happen before this part of the interpreter is reached and there just needs some magic incorrect values to bypass them, I don't know.

Since this fixes an ICE, and if the constants are indeed used, these 2 tests will turn into a hard error, I thought I'd open the PR anyways. And if ``@RalfJung`` you know of a way I could manage that (if you think that these tests are worth checking that the `throw_ub_format!` does indeed create const-ub errors as we expect) I'd be grateful.

For that reason, r? ``@RalfJung`` and cc ``@oli-obk.``
bors added a commit to rust-lang-ci/rust that referenced this pull request Jun 12, 2021
Rollup of 7 pull requests

Successful merges:

 - rust-lang#85800 (Fix some diagnostic issues with const_generics_defaults feature gate)
 - rust-lang#85823 (Do not suggest ampmut if rhs is already mutable)
 - rust-lang#86153 (Print dummy spans as `no-location`)
 - rust-lang#86174 (Detect incorrect vtable alignment during const eval)
 - rust-lang#86189 (Make `relate_type_and_mut` public)
 - rust-lang#86205 (Run full const-generics test for issue-72293)
 - rust-lang#86217 (Remove "generic type" in boxed.rs)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 91faabb into rust-lang:master Jun 12, 2021
@rustbot rustbot added this to the 1.54.0 milestone Jun 12, 2021
@lqd lqd deleted the const-ub-align branch June 12, 2021 08:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
5 participants