Skip to content

Creating a raw reference/pointer to an extern never value claims following code is unreachable #74840

Closed

Description

@Nemo157

I tried this code (playground):

#![feature(never_type)]
#![feature(raw_ref_macros)]
#![feature(raw_ref_op)]

extern {
    static FOO: !;
}

fn main() {
    dbg!(unsafe { core::ptr::raw_const!(FOO) });
    dbg!(unsafe { &raw const FOO });
}

I expected to see this happen: just a linker failure because FOO doesn't exist.

Instead, this happened: before the linker failure, rustc complained that code was unreachable.

warning: unreachable expression
  --> src/main.rs:10:19
   |
10 |     dbg!(unsafe { core::ptr::raw_const!(FOO) });
   |                   ^^^^^^^^^^^^^^^^^^^^^^---^
   |                   |                     |
   |                   |                     any code following this expression is unreachable
   |                   unreachable expression

warning: unreachable expression
  --> src/main.rs:11:19
   |
11 |     dbg!(unsafe { &raw const FOO });
   |                   ^^^^^^^^^^^---
   |                   |          |
   |                   |          any code following this expression is unreachable
   |                   unreachable expression

(The code should also build without the unsafe; that is caused by the same problem.)

Meta

1.47.0-nightly
(2020-07-26 6c8927b0cf80ceee1938)

cc @rust-lang/wg-unsafe-code-guidelines

Activity

  1. Nemo157 commented on Jul 27, 2020

    @Nemo157
    ContributorAuthor

    To step it up a notch, I then tried giving FOO an address pointing to data at least as valid as ! (playground):

    #![feature(never_type)]
    #![feature(raw_ref_macros)]
    #![feature(raw_ref_op)]
    
    extern {
        static FOO: !;
    }
    
    mod foo {
        #[no_mangle]
        static FOO: u32 = 5;
    }
    
    fn main() {
        dbg!(unsafe { core::ptr::raw_const!(FOO) });
        dbg!(unsafe { &raw const FOO });
    }

    I expected this to run and exit fine, since it is just printing the address of a symbol that definitely has an address.

    Instead, it ran and then SIGILLed (after giving the same warnings as above):

         Running `target/debug/foo`
    [src/main.rs:15] unsafe { core::ptr::raw_const!(FOO) } = 0x000055b8aacb1604
    [src/main.rs:16] unsafe { &raw const FOO } = 0x000055b8aacb1604
    [1]    662959 illegal hardware instruction (core dumped)  cargo run
    
  2. added
    A-never-typeArea: `!` type. For issues not specific to `!`, use A-uninhabited-types.
    F-raw_ref_op`#![feature(raw_ref_op)]`
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Jul 27, 2020
  3. RalfJung commented on Jul 28, 2020

    @RalfJung
    Member

    The relevant MIR looks like this:

            _2 = const {alloc0: &!};
            _1 = &raw const (*_2);

    @oli-obk looks like creating a raw ptr to a static goes through a reference, with the new scheme? Also Cc @matthewjasper

    FWIW I am not sure if this is really a bug; an explicitly uninhabited static seems at the very least highly suspicious to me. This is currently certainly not a blessed pattern. But the generated MIR is still odd.

  4. Nemo157 commented on Jul 28, 2020

    @Nemo157
    ContributorAuthor

    The context in which I noticed it was an attempt by @repnop at using them to pass the heap start/end addresses from the linker to a Rust program (discord convo). It seemed like a somewhat correct way to encode a symbol that has a known address, but should not be readable.

  5. RalfJung commented on Jul 28, 2020

    @RalfJung
    Member

    I think an inhabited ZST like () is a more appropriate way to encode that pattern. Also see the "opaque struct" section in the Nomicon.

  6. RalfJung commented on Jul 29, 2020

    @RalfJung
    Member

    Btw I think this should be merged with #74843; both are caused by the same odd MIR generation.

  7. oli-obk commented on Jul 29, 2020

    @oli-obk
    Contributor

    We're computing the type of the "address of" constant of statics in

    let ty = cx.tcx.static_ptr_ty(id);
    and that doesn't account for extern statics:
    pub fn static_ptr_ty(&self, def_id: DefId) -> Ty<'tcx> {
    // Make sure that any constants in the static's type are evaluated.
    let static_ty = self.normalize_erasing_regions(ty::ParamEnv::empty(), self.type_of(def_id));
    if self.is_mutable_static(def_id) {
    self.mk_mut_ptr(static_ty)
    } else {
    self.mk_imm_ref(self.lifetimes.re_erased, static_ty)
    }
    }

    I thought we had some more logic there a while back... oh well. We need an else if self.is_foreign_item(def_id) { self.mk_imm_ptr(static_ty) }... before the else, which should then give us exactly what we want

  8. oli-obk commented on Jul 29, 2020

    @oli-obk
    Contributor

    You can check this out by changing all your extern statics to be mutable, then you get the codegen you'd get with my suggested change above. Though the never type example still dies horribly, so there seems to be a bit more to the never type, even if there's never a reference to it.

    EDIT: I'm not sure what's causing this, but I'm guessing the deref projection for the reborrow, as just having a constant of raw pointer to never type doesn't cause any problems: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=fb80e957cebf8bf982ce50f914bd9236

  9. RalfJung commented on Aug 17, 2020

    @RalfJung
    Member

    Yeah, the problem is here already:

    _2 = const {alloc0: &!};
    

    We are embedding the const at reference type (note that &!), which is wrong.

    Maybe @matthewjasper knows more about how/why this happens.

  10. added
    requires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.
    on Aug 17, 2020
  11. oli-obk commented on Aug 17, 2020

    @oli-obk
    Contributor

    The fix is described in #74840 (comment) where you'll end up with a raw pointer in that constant. But there's some additional messiness around never types, but without doing the fix first, I don't know what it is exactly.

  12. added
    E-easyCall for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.
    on Aug 17, 2020
  13. 55 remaining items

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-never-typeArea: `!` type. For issues not specific to `!`, use A-uninhabited-types.A-raw-pointersArea: raw pointers, MaybeUninit, NonNullC-bugCategory: This is a bug.F-raw_ref_op`#![feature(raw_ref_op)]`T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.requires-nightlyThis issue requires a nightly compiler in some way. When possible, use a F-* label instead.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions