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

What is the UB behaviour of accessing *mut through &T #1227

Closed
EvanTheB opened this issue Jun 8, 2022 · 5 comments
Closed

What is the UB behaviour of accessing *mut through &T #1227

EvanTheB opened this issue Jun 8, 2022 · 5 comments

Comments

@EvanTheB
Copy link

EvanTheB commented Jun 8, 2022

* Mutating immutable data. All data inside a [`const`] item is immutable. Moreover, all

Mutating immutable data. All data inside a const item is immutable. Moreover, all data reached through a shared reference or data owned by an immutable binding is immutable, unless that data is contained within an UnsafeCell.

I was wondering how to use *mut pointers in more safe way, I asked this question: https://stackoverflow.com/questions/72541045

A user responded that I read this paragraph wrong, and that modifying data through both UnsafeCell<U> AND *mut U would not be UB. Does the above paragraph need to mention raw pointers?

@bjorn3
Copy link
Member

bjorn3 commented Jun 9, 2022

No, if the *mut U is derived from a shared reference without it being of type &UnsafeCell<U> or similar, writing through the *mut U is still UB.

@ChayimFriedman2
Copy link
Contributor

ChayimFriedman2 commented Jun 10, 2022

@bjorn3 I'm the bespoken user. The OP is talking about a case like:

struct Foo(*mut i32);

unsafe fn bar(v: &Foo) {
    *v.0 = 123;
}

Not something like:

struct Foo(i32);

unsafe fn bar(v: &Foo) {
    *(&v.0 as *const i32 as *mut i32) = 123;
}

Are you referring to that too, or have you talked about the cast case?

@bjorn3
Copy link
Member

bjorn3 commented Jun 10, 2022

I see. The first snippet is fine.

@EvanTheB
Copy link
Author

Thank you for your help.

Maybe what I am confused about is that rust does not consider the *mut T to 'own' the T. Nor is the T 'inside' the item. Nor is the T 'reached through' the reference.

I want treat my *mut T as owning the data, so I wrote a small wrapper:

/// This struct exists only to force *mut to be used in a mutable context
/// For example, to prevent this:
///
/// fn mutate(ptr: *mut T) {}
/// fn (&self) {
///     mutate(self.ptr)
/// }
///
#[derive(Debug)]
pub struct CPtr<T> {
    ptr: *mut T,
}

impl<T> CPtr<T> {
    pub fn new(ptr: *mut T) -> Self {
        Self { ptr }
    }

    pub fn as_ptr(&self) -> *const T {
        self.ptr
    }

    pub fn as_mut_ptr(&mut self) -> *mut T {
        self.ptr
    }
}

I am suprised such a wrapper does not already exist in the stdlib, so I suspect that it does and I have missed it.

@ChayimFriedman2
Copy link
Contributor

This wrapper is called Unique and is an internal implementation detail, although it is considered owned by rustc and Stacked Borrowed, at least currently. It is used to implement e.g. Box, but was deemed not common enough to be exposed.

ivanbakel pushed a commit to ivanbakel/reference that referenced this issue Jul 21, 2023
I personally found this description of UB confusing, since the use of
"reached" suggests that UB only happens for read bytes, and the
definition of immutability is not given, allowing for multiple
interpretations: does the "data" have to be immutable from the first
read? From the creation of the reference? Between reads from the
immutable accessor, but not otherwise? etc.

This clarifies the actual UB conditions, based on this Zulip
interaction:
https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/What.20exactly.20are.20.22immutable.22.20and.20.22reached.22.20in.20shared.20ref.20UB.3F
and this reference discussion:
rust-lang#1227
in two ways:
  * The definition of "data" is clarified to be stated in terms of
    bytes, in a way that should avoid ambiguity about which bytes are
    considered. Based on the GH issue, this clarification should also
    allow for use of a `*mut` pointer through a shared reference, which
    is not in itself UB. Based on the Zulip issue, the definition
    includes padding bytes, which may be surprising.
  * The definition of immutability & mutation for a set of bytes is
    clarified to mean forbidding *all* non-0-byte writes.
ivanbakel added a commit to ivanbakel/reference that referenced this issue Jul 24, 2023
I personally found this description of UB confusing, since the use of
"reached" suggests that UB only happens for read bytes, and the
definition of immutability is not given, allowing for multiple
interpretations: does the "data" have to be immutable from the first
read? From the creation of the reference? Between reads from the
immutable accessor, but not otherwise? etc.

This clarifies the actual UB conditions, based on this Zulip
interaction:
https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/What.20exactly.20are.20.22immutable.22.20and.20.22reached.22.20in.20shared.20ref.20UB.3F
and this reference discussion:
rust-lang#1227
in two ways:
  * The definition of "data" is clarified to be stated in terms of
    bytes, in a way that should avoid ambiguity about which bytes are
    considered. Based on the GH issue, this clarification should also
    allow for use of a `*mut` pointer through a shared reference, which
    is not in itself UB. Based on the Zulip issue, the definition
    includes padding bytes, which may be surprising.
  * The definition of immutability & mutation for a set of bytes is
    clarified to mean forbidding *all* non-0-byte writes.
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

3 participants