-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Fix unintentional UB in ui tests #107972
Fix unintentional UB in ui tests #107972
Conversation
(rustbot has picked a reviewer for you, use r? to override) |
let null_u32 = ptr::null::<u32>() as *const dyn Foo; | ||
|
||
assert_eq!("I'm an i32!", null_i32.foo()); | ||
assert_eq!("I'm a u32!", null_u32.foo()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that really UB? There's only raw pointers involved and they are not dereferenced?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also there is no unsafe code here I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
error: Undefined Behavior: dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
--> tests/ui/self/arbitrary_self_types_raw_pointer_trait.rs:40:31
|
40 | assert_eq!("I'm an i32!", null_i32.foo());
| ^^^^^^^^^^^^^^ dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an outside observer, I feel like this indicates a bug in Miri - there shouldn't be a dereference to call a raw-pointer receiver, I'd think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure where the deref is coming from, definitely needs investigation. UB from safe code is no good.^^
Tracking this at rust-lang/miri#2786
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dropped the changes to this file. If we decide this still needs to be changed (unlikely) I can always do another PR.
@@ -26,7 +26,7 @@ fn test_send_trait() { | |||
//~| HELP: add a dummy let to cause `fptr` to be fully captured | |||
*fptr.0 = 20; | |||
//~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0` | |||
} }); | |||
} }).join().unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed? Doesn't look like a UB fix. Is this about fixing memory leaks? Is that even a goal or should Miri be run with -Zmiri-ignore-leaks
when running ui tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without this join, the thread can execute after the function returns, which is a use-after-free.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh good point!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to give me too much credit here, I just looked at Miri's output :p
@@ -59,7 +59,7 @@ pub fn main() { | |||
} | |||
|
|||
let data: Box<Foo_<i32>> = Box::new(Foo_ { f: [1, 2, 3] }); | |||
let x: &Foo<i32> = mem::transmute(slice::from_raw_parts(&*data, 3)); | |||
let x: &Foo<i32> = mem::transmute(ptr::slice_from_raw_parts(&*data, 3)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I understand why this helps, given that both are transmuted to the same target type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The &Foo<i32>
is fine. The problem is the slice creation itself, because that call tries to build a slice of 3 elements from a pointer to a Box of 1 element.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh so the temporary slice actually pretends to have 3 Foo_<i32>
. Wtf, why? At that point one might just transmute a (ptr, usize)
directly without going through from_raw_parts...
Anyway, patch looks good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
transmute a
(ptr, usize)
That would require relying on the still-unstable slice layout 😉
But yeah I have no idea why anyone would want to write the code in this test. Considering the amount of code in the SIMD tests that were using a pointer to the first element as a pointer to the whole array it might not hurt for people to skim over the tests for some feature or another from time to time...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible to make a reference to a DST without relying on transmute
or assuming that any two slice-like fat pointers have the same layout by using the fact that as
preserves metadata. playground example
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's the sort of thing I would advise someone write, but this is a test... so I am not really sure what it is supposed to test. So I'm preserving as much of it as I can in the hope that works.
d44149f
to
5925400
Compare
…rister Fix unintentional UB in ui tests `@matthiaskrgr` found UB in a bunch of the ui tests. This PR fixes a batch of miscellaneous tests I didn't think needed reviewers from a particular part of the project.
…rister Fix unintentional UB in ui tests ``@matthiaskrgr`` found UB in a bunch of the ui tests. This PR fixes a batch of miscellaneous tests I didn't think needed reviewers from a particular part of the project.
unsafe { std::slice::from_raw_parts(self.data.as_ptr(), self.len) } | ||
unsafe { | ||
let ptr = (self as *const List<T>).cast::<usize>().add(1).cast::<T>(); | ||
std::slice::from_raw_parts(ptr, self.len) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this even doing? It looks like the original code would run into the &Header
issue, but that issue doesn't have a work-around in Stacked Borrows...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CI is failing because of padding that I failed to account for in my poorly-written, manual, offset_of!
.
I don't entirely follow your reasoning, but I think the code is UB before we even get to where you are seeing the &Header
pattern. The slice can't be constructed because the array is length 0:
error: Undefined Behavior: trying to retag from <3086> for SharedReadOnly permission at alloc1[0x8], but that tag does not exist in the borrow stack for this location
--> /home/ben/rust/library/core/src/slice/raw.rs:100:9
|
100 | &*ptr::slice_from_raw_parts(data, len)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| trying to retag from <3086> for SharedReadOnly permission at alloc1[0x8], but that tag does not exist in the borrow stack for this location
| this error occurs as part of retag at alloc1[0x8..0x38]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <3086> would have been created here, but this is a zero-size retag ([0x8..0x8]) so the tag in question does not exist anywhere
--> tests/ui/consts/const-eval/issue-91827-extern-types.rs:31:45
|
31 | unsafe { std::slice::from_raw_parts(self.data.as_ptr(), self.len) }
| ^^^^^^^^^^^^^^^^^^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added an even more horrifying implementation that passes
x run miri --args tests/ui/consts/const-eval/issue-91827-extern-types.rs --target=wasm32-unknown-unknown
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The empty array is the header, isn't it?
pub struct List<T> {
len: usize,
data: [T; 0],
tail: Opaque,
}
is basically a variable-sized array, serving as the header for ListImpl.
Also doesn't tail_offset
further down already compute the offset you need?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I entirely forgot that I made &Header work with extern types, so what is what is happening here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite understand why this is done in the way it is. Is there any value for the test to not just express this as let ptr = (&self.data) as *const T;
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
&
restricts provenance to that field, which has size 0, so the resulting reference is only valid for 0 bytes, which is not very useful.
But addr_of!(self.data)
should work.
4e04d6d
to
de01ea2
Compare
@bors r+ |
…iaskrgr Rollup of 10 pull requests Successful merges: - rust-lang#107034 (Migrating rustc_infer to session diagnostics (part 4)) - rust-lang#107972 (Fix unintentional UB in ui tests) - rust-lang#108010 (Make `InferCtxt::can_eq` and `InferCtxt::can_sub` return booleans) - rust-lang#108021 (make x look for x.py if shell script does not exist) - rust-lang#108047 (Use `target` instead of `machine` for mir interpreter integer handling.) - rust-lang#108049 (Don't suggest `#[doc(hidden)]` trait methods with matching return type) - rust-lang#108066 (Better names for illegal impl trait positions) - rust-lang#108076 (rustdoc: Use more let chain) - rust-lang#108088 (clarify correctness of `black_box`) - rust-lang#108094 (Demonstrate I/O in File examples) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
@matthiaskrgr found UB in a bunch of the ui tests. This PR fixes a batch of miscellaneous tests I didn't think needed reviewers from a particular part of the project.