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

Validity invariant for containers with unitialized data #393

Closed
hackaugusto opened this issue Feb 20, 2023 · 8 comments
Closed

Validity invariant for containers with unitialized data #393

hackaugusto opened this issue Feb 20, 2023 · 8 comments

Comments

@hackaugusto
Copy link

I hope this is a good channel to ask this, let me know if I should use something else :)

I was trying to grasp what is considered UB w.r.t. uninitialized data. My current understanding is that there are validity rules, and that by definition uninitialized data is not valid for any type except union and padding.

From what I understood from the reference UB happens when the value is produced, and it describes it as:

"Producing" a value happens any time a value is assigned to or read from a place

It seems there is an issue with that definition for code like this:

let mut v: Vec<i32> = Vec::with_capacity(1);
unsafe { v.set_len(1) }

IIUC that second line above should be UB, but the rule of producing doesn't seem to cover that, since there is no data being assigned or read.

My questions are: 1. is it correct to say the above is undefined behavior? 2. am I missing some other rules that would define the above as UB?

Thanks!

@RalfJung
Copy link
Member

RalfJung commented Feb 20, 2023

This code is not intended to be (language-level) UB. It violates safety requirements but not validity requirements. This means that calling other safe Vec operations later can cause UB (e.g. v.pop()) -- but that is not pops fault, it is the fault of the bad set_len call. set_len brought Vec into a "bad state" where its key invariants no longer hold, but those invariants are not known to the language or compiler, so violating them does not cause immediate UB.

Also see this blog post on validity vs safety invariants.

@hackaugusto
Copy link
Author

hackaugusto commented Feb 20, 2023

@RalfJung thanks for the reply!

Oh, okay, I was interpreting v.set_len(1) to be "the same" as v.push(MaybeUninit::uninit().assume_init()) and that it wasn't defined as UB was just a quirky of the producing definition.

But what you're saying is that I can have memory for a type i32 with uninitialized bits as long as I don't look at it. So the code below does not have UB?

let mut v: Vec<i32> = Vec::with_capacity(1);
unsafe { v.set_len(1) }
v[0] = 1i32;

edit: by the way, I read your blog a few times already, great stuff! What confused me was that I assumed that the validity rules applied to the type i32, so I understood that I couldn't ever have memory place that broke a type validity requirements. iow, I interpreted that not as a safety issue in vec but as a validity issue for the i32 memory. Anyway, thanks again :)

@deltragon
Copy link

But what you're saying is that I can have memory for a type i32 with uninitialized bits as long as I don't look at it. So the code below does not have UB?

let mut v: Vec<i32> = Vec::with_capacity(1);
unsafe { v.set_len(1) }
v[0] = 1i32;

The third line here adds an additional question, as it creates an &mut i32 (through the IndexMut::index_mut() desugaring), which points to uninitialized memory. This seems to be allowed if I understand #346 correctly, but is not officially documented yet. (Additionally, I'm not sure if the write to &mut uninit is allowed).

@hackaugusto
Copy link
Author

@deltragon thanks a lot for the link, that clears up a lot! I'm closing the issue since my question was answered.

@deltragon
Copy link

@hackaugusto Note also that it is very easy to run miri in the playground (it's under tools in the top right): snippet
This might not report undefined behaviour about things that haven't been decided yet, but can help a lot when trying to answer "is this UB?". (Eg, here is the same snippet but with String , which miri (rightly) reports as UB.)

@RalfJung
Copy link
Member

RalfJung commented Feb 20, 2023 via email

@saethlin
Copy link
Member

here is the same snippet but with String

This is the same link as the other link in your reply, and String doesn't have a set_len, so I'm not even sure what you're trying to point out here.

@deltragon
Copy link

@saethlin Sorry, this link was supposed to be this.
I was just trying to point how for Vec<String>, dropping the previous (uninitialized) data can trigger UB when assigning to &mut uninit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants