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

[tracking issue] dereferencing raw pointers inside constants (const_raw_ptr_deref) #51911

Closed
Tracked by #16
oli-obk opened this issue Jun 29, 2018 · 11 comments · Fixed by #89551
Closed
Tracked by #16

[tracking issue] dereferencing raw pointers inside constants (const_raw_ptr_deref) #51911

oli-obk opened this issue Jun 29, 2018 · 11 comments · Fixed by #89551
Labels
A-const-eval Area: Constant evaluation (MIR interpretation) A-const-fn Area: const fn foo(..) {..}. Pure functions which can be applied at compile time. B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@oli-obk
Copy link
Contributor

oli-obk commented Jun 29, 2018

The const_raw_ptr_deref feature allows dereferencing raw pointers inside constants.

This can already be emulated via union transmuting raw pointers to references and referencing the references. This feature gate helps us experiment with the semantics of actually dereferencing normally.

Blocked on rust-lang/const-eval#14.

@oli-obk oli-obk added A-const-fn Area: const fn foo(..) {..}. Pure functions which can be applied at compile time. A-const-eval Area: Constant evaluation (MIR interpretation) labels Jun 29, 2018
@ketsuban
Copy link
Contributor

What does this feature actually do?

To my mind the primary use case for this is going to be static references to memory-mapped I/O registers in embedded development; however, the obvious thing

static REG_DEBUG_ENABLE: &'static u16 = unsafe { &*(0x4fff780 as *mut u16) };

gets me the error

this static likely exhibits undefined behavior

type validation failed: encountered integer pointer in non-ZST reference

note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior

which suggests that it's constructing 0x4fff780 as *mut u16 on the host machine at compile-time, dereferencing it and substituting what it found so that dereferencing REG_DEBUG_ENABLE on the target machine at runtime produces the same value, when I just wanted a typecast.

@oli-obk
Copy link
Contributor Author

oli-obk commented Oct 24, 2018

which suggests that it's constructing 0x4fff780 as *mut u16 on the host machine at compile-time,

Not quite. That happens transparently. The error comes from the sanity checks on the value of constants and statics.

This feature is for when you converted a safe reference to a raw pointer, allowing you to turn it back into a safe reference.

What you are trying to do is simply not allowed (yet?).

Side-note: Additionally it seems dangerous to create references to registers without using volatile accessors. A &'static u16 guarantees (to best of my knowledge) that the value pointed at will not change at all during the entire runtime of your program.

@hanna-kruppe
Copy link
Contributor

hanna-kruppe commented Oct 24, 2018

Yes, &'static u16 is wrong for MMIO, you'd want something like &'static VolatileCell<u16> (for a suitable definition of VolatileCell), though that doesn't change anything with respect to the pointer casting that needs to happen.

@ketsuban
Copy link
Contributor

I wasn't being very careful when throwing out an example. Pretend I used &mut instead and waved my hands about volatile accesses.

(Sidenote: I'm surprised VolatileCell isn't provided in the standard library.)

@oli-obk
Copy link
Contributor Author

oli-obk commented Oct 24, 2018

&'static mut T is definitely UB here.

And once you are at the VolatileCell level, you might as well make that a ZST with a phantomdata on your concrete type and everything will work out nicely.

@ketsuban
Copy link
Contributor

Okay, now I'm lost. It sounds like you just told me it's undefined behaviour to access system memory unless I allocate something on the stack first.

@oli-obk
Copy link
Contributor Author

oli-obk commented Oct 25, 2018

If you had a way to create &'static mut T from an integer safely, then you could do that twice and thus end up with two &mut T to the same address. This is UB in Rust.

What I was suggesting was that you'd use

#![feature(const_raw_ptr_deref)]

const FOO: &'static VolatileCell<u32> = unsafe { &*(0x4fff780 as *const VolatileCell<u32>) };
struct VolatileCell<T: Copy> {
    marker: std::marker::PhantomData<T>,
}
impl<T: Copy> VolatileCell<T> {
    fn read(&self) -> T {
        unsafe {
            std::ptr::read_volatile(self as *const _ as *const T)
        }
    }
}

to manage the access to device addresses

@Centril Centril added T-lang Relevant to the language team, which will review and decide on the PR/issue. C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. labels Dec 1, 2018
@alex
Copy link
Member

alex commented Sep 21, 2019

FWIW, the use case I have for this is to create #[repr(transparent)] DSTs as consts:

#[repr(transparent)]
pub struct CStr(str);

impl CStr {
    /// Creates a new CStr from a str without performing any additional checks. `data` _must_ end
    /// with a NUL byte, and should only have only a single NUL byte, or the string will be
    /// truncated.
    pub const unsafe fn new_unchecked(data: &str) -> &CStr {
        &*(data as *const str as *const CStr)
    }
}

@jonas-schievink jonas-schievink added the B-unstable Blocker: Implemented in the nightly compiler and unstable. label Nov 26, 2019
@eira-fransham
Copy link

Related: every function in std::ptr should be const. AFAICT there's no reason for them not to be.

@RalfJung
Copy link
Member

Mirroring some discussion from #86722 (not sure why that happened on the other tracking issue...):

My only concern with const raw pointer deref is how and where to document that type-punning loads (i.e., raw-ptr-based transmutes) of pointers to integers are considered UB during CTFE.

But just from an implementation perspective I can't think of any reason how raw ptr deref could screw us now that we have stabilized transmutes and union field accesses. The reason we only stabilized unions and transmute first is that those were already allowed in const (outside const fn).

@jhpratt
Copy link
Member

jhpratt commented Oct 5, 2021

This should only be *const T, not *mut T, right?

Edit: Tentatively moving ahead with stabilizing *const T. *mut T will be placed behind the const_raw_mut_ptr_deref feature gate.

Edit 2: PR is up. #89551

@bors bors closed this as completed in d212d90 Nov 13, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const-eval Area: Constant evaluation (MIR interpretation) A-const-fn Area: const fn foo(..) {..}. Pure functions which can be applied at compile time. B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: A tracking issue for an RFC or an unstable feature. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants