#Fixing UB in random crates
4312 messages · Page 5 of 5 (latest)
do we emit nounef on integers for -Zstrict-validity-checks or however it was called
no, that flag doesn't change noundef gen
it could
but that would be adding UB
to some programs
so, seemed unwise
doesn't it panic though on ub
hmm, well, only on mem::unini
there are other ways to get uninit integers
Yes but people don't do those and think it's okay
like MaybeUninit::uninit().assume_init() which is what i do now if i need to get an uninit value that has noundef
(for demo purposes ofc, not prod code :P)
I was more thinking of malloc
that too
malloc doesn't return a value and people know that reading from it is bad
maybe aggressive vec::set_len
But I don't think this could cause issues usually
Even with noundef
idk about set_len
it does say the set_len is UB if the values aren't init
That's a fun/not fun case to me
but
it feels more like a safety invariant?
and like
and the library does not care
maybe
Drop cares
Yeah exactly
hmmm how do i get a noundef load in C
Lulw
oh also @haughty mica are your asan run results public
or are they private for "this is actually a lot of real issues"
iirc you were doing asan runs?
They are not put up anywhere, more for "I don't know where to put them" reasons
miri.saethlin.dev/asan/ub 
Here are the ASan logs
I use rg + less -CR logs/the-crate to search and browse them
You will probably mostly find the LLVM bug that I reported, and I think I've patched all the other exciting ones
I really need to write my talk and plan my travel for RustConf
That is to say, I won't be doing this sort of stuff for a bit
I wish rust conf wasn't on the other side of the world 
finally, sound rustc_arena https://github.com/rust-lang/rust/pull/97711#pullrequestreview-1030711579
Only took a month on my side 
If it's just money that's an issue, we can fix that
I'm trying to get work to buy me a ticket for remote viewing. But it would be nice to go in person 
nah it's more about time
The magic crab told me that Zürich would be a really good location for RustConf 
I would defo fly to zurich for a conf (admittedly, it's just an hour away so 🤷)
i would also go to Zürich for a conf 
they had one there in 2016 i think

I would also fly there
okay
strict uninit checks checking by simply making an instance of the type and then asking "hey are you valid" is Being Worked On
and it's actually simpler
?miri
#![feature(arbitrary_enum_discriminant)]
#[repr(u8)]
enum InvalidAtZero {
Zero(std::num::NonZeroU16) = 0,
One(u8) = 1,
}
let _: InvalidAtZero = unsafe { std::mem::zeroed() };
warning: variants `Zero` and `One` are never constructed
--> src/main.rs:5:3
|
4 | enum InvalidAtZero {
| ------------- variants in this enum
5 | Zero(std::num::NonZeroU16) = 0,
| ^^^^
6 | One(u8) = 1,
| ^^^
|
= note: `#[warn(dead_code)]` on by default
error: Undefined Behavior: constructing invalid value at .value.<enum-variant(Zero)>.0: encountered 0, but expected something greater or equal to 1
--> src/main.rs:9:33
|
9 | let _: InvalidAtZero = unsafe { std::mem::zeroed() };
| ^^^^^^^^^^^^^^^^^^ constructing invalid value at .value.<enum-variant(Zero)>.0: encountered 0, but expected something greater or equal to 1
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: backtrace:
= note: inside `main` at src/main.rs:9:33
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
yeah
miri catches this
but strict-init-checks can't
so once we remove the hacks to keep shit working, we can simplify the code for mem::uninit/zeroed checks by simply going "just make an instance of the type and ask if it's valid"
MSan has an "eager checks" option which checks noundef arguments and return values. Rustc currently doesn't enable this option, but probably should?
👀
yes
yes it should
Hi! I'm the author of second-stack - a fast allocator for large, short-lived or variable sized allocations (similar to arena allocators like bumpalo, but with stack semantics instead of arena semantics... allowing for nice things like Drop support).
I'd like to know generally how to get a code review for potential UB.
The main concern for me about UB is less about the threadlocal APIs (though, those require care and should be reviewed) and more with the Stack type which affords more control. Because the API uses interior mutability, there is sort of a potential for shared mutability. But, I think that potential is removed because Stack is both !Send and !Sync due to the UnsafeCell contained within.
Is this the right place to check?
miri complains about the soak test (i lowered the thread count to make miri actually able to complete running it)
the full stack trace, i can probably minimise this down to a simpler test case
@tough leaf Thanks. I'll figure out how to run the test with Miri.
let handle_count = if cfg!(miri) { 4 } else { 64 };
let mut handles = Vec::with_capacity(handle_count);
for _ in 0..handle_count {
https://github.com/That3Percent/second-stack/blob/393af07e6db4ccc2863a18b10b3066ea4b6e26b6/src/lib.rs#L67 you're not allowed to read ZSTs from freed memory
I may not be understanding, but it looks to me like that Vec at tmp is never freed. (Maybe I should free it after calling f) So, we do not "read ZSTs from freed memory".
yeah it's freed when you return
but
vec hands out "dangling" memory
for zsts
but also
oh it's only read during the the function
it's fine
Yeah, I see that as_mut_ptr does not consume self, so this would be freed after f is called.
i guess
I can do that, no problem. Thanks.
and the Miri issue that 522 found
When writing unsafe code, always run miri
(also I don't think this channel is for requesting unsafe code review, #dark-arts is just fine but whatever)
Yes, I'm trying to reproduce that issue now but haven't seen it come up. (The test is randomized to include every possible problem that I couldn't think of)
I asked there first, and they instructed me to come here.
Run Miri, and run with ASan + build-std
How?
Both are nightly-only
Miri is
rustup component add miri
cargo miri test
ASan is
RUSTDOCFLAGS=-Zsanitizer=address ASAN_OPTIONS="detect_leaks=0 detect_stack_use_after_return=1" RUSTFLAGS=-Zsanitizer=address cargo test -Zbuild-std --target=$(rustc --version --verbose | grep ^host: | cut -d' ' -f2)
I feel like people talk or joke about me detecting UB or finding UB and whatnot but all I do is run the above
Very rarely would I audit code by hand, not that it is a bad idea to do that
I'm not having much luck with the last part:
= note: clang: warning: argument unused during compilation: '-pie' [-Wunused-command-line-argument]
ld: warning: directory not found for option '-L/Users/zacharyburns/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-unknown-linux-gnu/lib'
ld: warning: directory not found for option '-L/Users/zacharyburns/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-unknown-linux-gnu/lib'
ld: unknown option: --as-needed
clang: error: linker command failed with exit code 1 (use -v to see invocation)
me with a fuzzer
cargo fuzz run is all i do
is it not?
Apple struggle
Ah... because I have a macbook. I didn't ask for this, my employer gave it to me.
Oh
I see
no no no
I have a --target in there that you need to switch to your apple host target
How does this fair on Windows? I have a Windows machine as well.
I should really replace that with a subshell thingy that extracts the host target
or make a PR to rustc to just automatically use the host target if it's not specified
UGH
rustc knows the host target doesn't it
There's an issue about this on the std-aware-cargo repo I think
In your snippet you have... cargo test -Zbuild-std --target=$(rustc --version --verbose | grep ^host: | cut -d' ' -f2)
Yes I just edited it
Shouls this be cargo miri test...?
No
does windows even do subshell thingies?
Aaaaaaaaaaaaaaa
Ok. What's the point of running the test with std built locally without miri?
(Sorry, learning and asking basic questions)
The standard library is by default built
- Without the ASan instrumentation
- Without debug assertions
Because it is distributed in a compiled form, and only one compiled form, which is the default release profile.
When using sanitizers you should always use -Zbuild-std, because the sanitizers alter code generation to insert a bunch of extra stuff all over the place, and if part of your program doesn't have the extra stuff in it, things can go awry. You can get false positives and false negatives.
Additionally, turning on -Zbuild-std and running in debug mode gets you all the debug assertions in the standard library which I wrote many of so I love them very much. They also catch some things that people do which they think are valid but aren't if you read the docs. It's just easy to intuit the wrong thing, depending on your background.
I have plenty of patience for beginnner questions, but -Zbuild-std and then the what about Windows is really hitting all the things that frustrate me
-Zbuild-std should be the default, it's a huge mistake that it wasn't, I think it was motivated by a lot of the wrong reasons, and the fact that you need to pass the target explicitly is just an unnecessary thing to top it all off
I apologize for touching on some sore spots there.
Just trying to write the best libraries I can.
I was pulling your leg a lil there too
The day has been a rollercoaster thats for sure
fwiw I totally agree, -Zbuild-std should've been the default for a long time
it's immensely useful for so many totally different things
I'm so haunted by it honestly, ever since I randomly saw the tweet from Oxide about how my debug assertions caught a bug in heapless that caused their bootloader to hang
I think a lot of embedded people use it and simply don't talk about it much because they are used to being uncared-for
Bryan used the term "self-sufficient" when I asked about it
I think that's his spin tbh
it does inflate the build time but like
miri builds std
and is fine, because it only needs to do it on every update
there's no reason why rust can't cache the built-std in some common place
Yes exactly
And if we're so worried about download size, shipping the source is smaller
gee if only there was some kind of sound caching mechanism for build artefacts
target isn't global (and maybe it should be?)
i do it
also, the average crate has 250000 dependencies
what's "global"?
i have my $CARGO_TARGET_DIR set to ~/.cache/cargo/target
so all build artifacts go in there
I just no longer have any patience for tooling that makes me do cache invalidation manually
"oh I changed this part I rarely change, now I need to delete this and run two commands instead of the one"
if you cache things, please key the cache by all the things that are relevant
oof I just remembered having to mess around with xargo
and then cargo-xbuild
Miri uses xargo
wait what
Yuuuuuuup
I do not think so
xargo has been dead for years
whats this?
sorry
didnt know there was an alternative to xargo
it's just a flag to cargo
-Zbuild-std
it's built-in, but it's "experimental"
I've been using it for all kinds of weird crap pretty much since the day it existed (that's years ago) with 0 issues. It works scarily well
most robust experimental feature I've ever seen
(it's possible I've just been lucky)
It has edge cases where it is totally broken
The one I'm familiar with is the conflict with -Zinstrument-coverage
not that I'm surprised about that
but also, not building std is similarly broken in edge cases 
Yeah that's why we shove everything into the nightly-only category so we can pretend it isn't broken and is just shipped in an incomplete state or something
Really I'm just worried about the understaffing of the cargo team
bitvec does many int2ptr casts 
I'm back with a minified UB from second-stack that I would like to better understand.
fn minified_ub() {
unsafe {
let mut vec = Vec::<u8>::with_capacity(2);
let base_ptr = vec.as_mut_ptr();
std::mem::forget(vec);
let mut slice = slice::from_raw_parts_mut(base_ptr as *mut MaybeUninit<u8>, 1);
// Re-assign to grow the slice from the same allocation.
// Miri does not complain if we replace "slice.as_mut_ptr()" with "base_ptr"
slice = slice::from_raw_parts_mut(slice.as_mut_ptr(), 2);
}
}
Here we: 1) crate an allocation, 2) Get a slice of it, 3) later decide we want to reserve more of the allocation, so overwrite our mutable reference with an expanded view of the same allocation
What's the Miri error?
test minified ... error: Undefined Behavior: trying to reborrow <192429> for Unique permission at alloc80483[0x1], but that tag does not exist in the borrow stack for this location
--> /Users/zacharyburns/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/slice/raw.rs:141:9
|
141 | &mut *ptr::slice_from_raw_parts_mut(data, len)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| trying to reborrow <192429> for Unique permission at alloc80483[0x1], but that tag does not exist in the borrow stack for this location
| this error occurs as part of a reborrow at alloc80483[0x0..0x2]
|
= 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: <192429> was created by a retag at offsets [0x0..0x1]
Ah right
So
slice has a length of 1
So it's only allowed to access a single element
And not the second element
When you get the pointer from the slice, that pointer still only has access to this one element
So you can't create a slice of length 2 from it
But with the base_ptr, that's fine, since it has permissions to access the whole memory of the vec
Ok. So, I need to keep around a copy of the base ptr (even though it has the same value as the slice ptr). Correct?
That would solve the problem
Another solution is to not use reference sliced but pointer slices instead
This permission (provenance) shrinking is a property of references
Using *mut [T] wouldn't have the problem
Yes, I understand now. Thanks.
With std::ptr::slice_from_raw_parts
Is this worth yanking my previously published versions of the crate for or can I just fix the code, publish a new release and move on?
just fix it
no yanking required
that UB isn't dangerous right now and won't break horribly
Great, thanks for your and everyone else's help today and yesterday! I'll go fix it now.
I think you chopped of the error that explains what is going on
Or, well, at least the span part
I should really improve these errors
It's a pretty easy leap to add a "because" or something in there
BTW you're supposed to be able to put together what nils explained because
trying to reborrow <192429> for Unique permission at alloc80483[0x1]
help: <192429> was created by a retag at offsets [0x0..0x1]
So you're supposed to be able to realize that the access using the tag is at an offset beyond the retag that created the tag
But I am realizing more how many people are using Miri when they are not familiar with these concepts already, so I will have to convince Ralf that is the case
though if it's semver compatible, there's no major reason not to yank
but that more applies to heavily used crates tbh
Yeah. So, this is unfortunate, but I agree with the assessment that not everyone will be familiar with stacked borrows in-depth. Ideally everyone who writes unsafe code is intimately familiar. But in practice, the content is dense and people have to weigh the tradeoff of learning that against other constraints on their time.
Oof I don't know about that
Tools should be helping, but in unsafe code there is absolutely no substitute for learning the rules properly. That is the nature of unsafe programming. We could tip the scales a bit by trying to make it easier to learn, but that doesn't change the fact that you do need to learn. If your code passes the borrow checker, it doesn't have borrowing issues. If your code passes Miri... 🤷 it might still have aliasing problems.
I also think Ralf actually expects people to click that URL and read about Stacked Borrows, and I think more prompting is required
For sure. To counter that though, there were multiple previous versions of stacked borrows that were wrong (which people may have read and carried confusion with), and a ton of the content out there comes with several caveats and statements of ambiguity/unfinished ideas.
Even the very error message says "but the Stacked Borrows rules it violated are still experimental"
There's a lot of prominent people (including Ralf) who often answer questions about UB with "I don't know, that's being worked out".
if you want tips regarding the error messages: I'd say people don't even read those offsets, even if they show the precise problem. This is probably because they are a few important numbers right behind some pretty irrelevant numbers (allocid). They should be displayed more clearly, especially in cases like these where they are the key to understanding the problem
Well, it's numbers next to numbers for the top one
The allocid is not irrelevant though that's the thing
yes, it should be displayed, but it's not as relevant as the offset here
I think the URL would be more helpful if it pointed to a relevant part of the stacked borrows spec. I did click the URL, and then reacted with oh yeah, I remember trying to read this last year and it was a mess. (I think it was a previous version of the spec though).
That is useful feedback
Can we reliably identify specific "things" that people do wrong under SB usefully?
Absolutely
like calling as_mut_ptr twice as one Thing
That too
and then have basicaly rustc --explain
but for SB
and probably a link to a document on the miri repo
Hm that's a lot of work for something Ralf wants to fix
preferably people would actually use it though
has anyone ever used rustc --explain ?
i haven't, tbh
same
The explanations rarely contain more information than the errors themselves
And I've never found them useful if they do
i think the general "here's what you did wrong generally speaking, here's what that looks like, here's how to fix it"
is not a bad idea
especially since errors tend to be very general
How to fix it is very hard
I think just a better error message is much higher yield
yeah
(BTW) I'll end up reading the whole spec again personally... none of the sentiment I have expressed about aversion to do so should be taken as a "correct" stance, I just think that people will be lazy.
i mean i haven't read it
lol
I totally read it once but it didn't help me understand
That doesn't mean I'm ignorant of the rules
I've read it once and while it has helped me know a bit more about SB, it was not the reason I could help you above, that was just by experience 
I spend most of my evenings editing the code that implements SB, and that's how I know it
saethlin knows like the entire Miri stacked borrows checking source code 
which is just a spec but written in rust instead of english
Sure. But we can expect basically 0 users of unsafe to have that same experience as "I am Saethlin"
I agree in general
But I do suggest that people read the code that implements SB instead of the spec because that's what I found accessible
That's a high bar. Also maybe link to the code in the error message then?
The core implementation is 1k lines of Rust
If that's the standard, maybe link to the code.
There's a lot of demand to write unsafe code. But the expectations for doing so need to be clear.
Uhhhhhhh
Hunh
I think there is an expectation mismatch here
I expect that people writing C know the C language and all its UB because they are constantly on the hook to avoid it.
I think that is considerably harder than reading any reference material for Stacked Borrows
I think we're generally in agreement about most things (for example, that people should be reasonably expected to learn the requirements of using unsafe). But, consider the story from my perspective... I've been using Rust for ~4 years, and programming for ~20. I've read the nomicon. I tried to read SB (when it was more like a PhD thesis). I have proactively attempted to learn the rules and abide by them. By the admission of everyone here, the SB spec linked to is "unhelpful". So the question I ask is, for someone on the journey to learning unsafe, how are they supposed to discover the path? Apparently the path is to read the Miri source. Somewhere in the documentation I would hope that a learner could discover a link to that source. Without a link, I would never have guessed that the most fruitful path would be to try to locate and understand the source code implementing SB. Will I read it? Sure. But I have no expectation that other writers of unsafe will discover the path.
Please accept this criticism with grace. I very much appreciate everything that all of you do.
my path was hanging out in #dark-arts and using unsafe myself with miri
there's a pretty good high level explanation of SB at the end of "learning rust with entirely too many linked lists"
but it's maybe a bit too high level
I read most of that too... but I think that was a long while ago before I was interested in unsafe. My bad.
Also, the more that the path (whichever path it is) respects people's time, the more likely it will be that people write correct unsafe. Everybody has a breaking point.
There's a curve there.
i have yet to see a cute diagram about sb. it's always either rust or english. that would probably help as well
imagine a diagram where all the tags are getting thrown out the stack when it's borrowed as &mut and they're all like :O
Also know that I understand that there are constraints on your time, and no-one is obligated to do anything. I'm just trying to establish consensus that the situation for learning unsafe could be improved rather than landing at "well, you should have known to read the source".
Also, ❤️ to everyone here.
There is interest in an SB visualizer but the amount of data gets out of hand very fast
I'm sure there are ways to deal with it but we have 3 people with no experience in that kind of data vis in the conversation
@tough leaf Have you heard of our lord and savior https://github.com/AFLplusplus/LibAFL?
Also https://github.com/microsoft/lain is neat, could help write custom stuff
(there's a post called "fuzzing random crates" here by 522, might be relevant
)
seize data race is fixed 
Now that my optimization patches have landed, I'm re-running all the crates I know of that OOM or time out.
This has so far gone exactly how you would expect
UB? Or more OOMs?
I OOMed my system
Fixed my run config, now I'm just dancing on the edge
The re-running of the timeouts is going to take about 3 days

btw @haughty mica the perf run went through and it went great! https://github.com/rust-lang/rust/pull/99527#issuecomment-1190860636
gonna hijack this thread since the SB thread is busy
Although maybe #957720175619215380 would be better but whatever 
