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 of raw pointers #75

Closed
RalfJung opened this issue Jan 10, 2019 · 7 comments
Closed

Validity of raw pointers #75

RalfJung opened this issue Jan 10, 2019 · 7 comments
Labels
A-validity Topic: Related to validity invariants

Comments

@RalfJung
Copy link
Member

RalfJung commented Jan 10, 2019

Discussing the validity invariant of raw pointers.

For pointers to sized types, this should probably be the same as the invariant for usize -- see the integer topic for discussing whether uninitialized bits are allowed or not.

For pointers to unsized types, there is an additional question: to what extent does the metadata have to be initialized/valid? Do we require it to be "valid" enough to determine size and alignment, e.g. do we require that the vtable pointer actually point to allocated memory?

@RalfJung RalfJung added active discussion topic A-validity Topic: Related to validity invariants labels Jan 10, 2019
@nikomatsakis
Copy link
Contributor

For pointers to sized types, this should probably be the same as the invariant for usize -- see the integer topic for discussing whether uninitialized bits are allowed or not.

Agreed, I think a cast from raw pointer to usize (and vice versa) should not be UB.

There is an additional question: to what extent does the metadata have to be initialized/valid? Do we require it to be "valid" enough to determine size and alignment, e.g. do we require that the vtable pointer actually point to allocated memory?

This is worth drilling a bit more into. I know I've had conversations with @eddyb, @Manishearth, @withoutboats, and a few others about this, and I'd love to hear more from them.

I believe that @Manishearth's argument boiled down to "it's really useful to be able to create the equivalent of a NULL pointer: a kind of 'universally valid' value that you know will be overwritten before the reference is ultimately used". I find this a compelling argument: we should make sure we can support that. I suppose the answer is that you can use MaybeUninit, though that definitely comes at an ergonomic cost.

I think it's certainly an option to say that the "metadata must always be valid". It'd be good to drill into the reasons we might want this to be true. Perhaps it's helpful to list out the times we need metadata to be valid?

Some examples I can think of:

  • Computing the alignment of a particular reference
  • Figuring out the offset of fields in a struct (that can depend on the alignment)
  • Invoking methods from a trait (particularly once we support fn foo(*mut self) methods, which are presently blocked on resolving this discussion)

Mostly these do seem to be tied to discrete actions in the code, though, and hence point to an invariant that could be enforced at the point of use.

@Amanieu
Copy link
Member

Amanieu commented Jan 31, 2019

Mostly these do seem to be tied to discrete actions in the code, though, and hence point to an invariant that could be enforced at the point of use.

I would argue that since all of these examples require references, valid metadata should only be needed for references while raw pointers (as long as they are not dereferenced) would be allowed to have invalid/null metadata.

Are there any cases where we need to access the metadata of a raw pointer without first turning it into a reference?

@SimonSapin
Copy link

I think @RalfJung mentioned before we could/should have variants of mem::size_of_val and mem::align_of_val that take a raw pointer instead of a reference, for example for use in Box’s destructor https://github.com/rust-lang/rust/blob/8a0e5faec7f62e3cfd88d6625ce213d93b061305/src/liballoc/alloc.rs#L195-L196

@gnzlbg
Copy link
Contributor

gnzlbg commented Feb 4, 2019

If we accept uninitialized bits as being a valid representation for usize, I wonder how that would impact what valid means for thin raw pointers and raw pointers in general. It might be weird to say that *mut i32 can be uninitialized, but that *mut dyn Trait cannot (or at least that some part of it cannot be, like e.g. the pointer to the vtable). We would be adding another rule that's not simple: instead of it being all raw pointers can / can't be uninitialized, we are adding a rule of the form: "some" raw pointers can be uninitialized.

@Amanieu
Copy link
Member

Amanieu commented Feb 4, 2019

It makes sense if you consider that we are only keeping mem::uninitialized valid for a limited set of types to maintain backwards compatibility with existing code. The majority of uses of mem::uninitialized are used with C structs which are then initialized through an FFI call, where fat pointers will not be used.

@RalfJung
Copy link
Member Author

RalfJung commented Feb 5, 2019

I assume if we allow uninitialized integers, we will also allow uninitialized data in the metadata of a raw pointer. I see no harm in that.

@JakobDegen
Copy link
Contributor

Closing, partially answered partially in favor of #166

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-validity Topic: Related to validity invariants
Projects
None yet
Development

No branches or pull requests

6 participants