Skip to content

with_exposed_provenance(0).with_addr(addr) is compiled as gep null #131741

Open
@saethlin

Description

@saethlin

Miri executes this program without error, but I believe our lowering to LLVM IR adds UB:

#![feature(strict_provenance, exposed_provenance)]
#[no_mangle]
pub fn from_exposed_null(addr: usize) -> *const u8 {
    let ptr = 0x0 as *const u8;
    ptr.with_addr(addr)
}

// Main for Miri
fn main() {
    let data = 0u8;
    let addr = std::ptr::addr_of!(data).expose_provenance();
    let ptr = from_exposed_null(addr);
    unsafe {
        println!("{}", *ptr);
    }
}

With optimizations enabled, LLVM cleans up from_exposed_null to

define noalias noundef ptr @from_exposed_null(i64 noundef %addr) unnamed_addr #0 !dbg !7 {
  %0 = getelementptr i8, ptr null, i64 %addr, !dbg !12
  ret ptr %0, !dbg !28
}

With -Cno-prepopulate-passes I believe the codegen also returns a pointer that definitely has no provenance, though it's just harder to read.
godbolt: https://godbolt.org/z/s414rfK44

I think this happens because codegen is implicitly const-propagating through the int-to-pointer cast via OperandValue. At least I don't see any other way to get from this MIR:

    bb0: {
        _2 = const 0_usize as *const u8 (PointerWithExposedProvenance);
        StorageLive(_3);
        StorageLive(_5);
        StorageLive(_6);
        StorageLive(_4);
        _4 = copy _2 as usize (Transmute);
        _3 = move _4 as isize (IntToInt);
        StorageDead(_4);
        _5 = copy _1 as isize (IntToInt);
        _6 = Sub(copy _5, copy _3);
        _0 = arith_offset::<u8>(move _2, move _6) -> [return: bb1, unwind unreachable];
    }

    bb1: {
        StorageLive(_7);
        _7 = copy _0 as *const () (PtrToPtr);
        StorageDead(_7);
        StorageDead(_6);
        StorageDead(_5);
        StorageDead(_3);
        return;
    }

To this LLVM IR

define noundef ptr @from_exposed_null(i64 noundef %addr) unnamed_addr #0 !dbg !7 {
start:
  %0 = alloca [8 x i8], align 8, !dbg !12
  %offset = sub i64 %addr, 0, !dbg !12
  call void @llvm.lifetime.start.p0(i64 8, ptr %0), !dbg !28
  %1 = getelementptr i8, ptr null, i64 %offset, !dbg !28
  store ptr %1, ptr %0, align 8, !dbg !28
  %self = load ptr, ptr %0, align 8, !dbg !28
  call void @llvm.lifetime.end.p0(i64 8, ptr %0), !dbg !28
  ret ptr %self, !dbg !34
}

godbolt: https://godbolt.org/z/MjPvcdj9h

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-strict-provenanceArea: Strict provenance for raw pointersC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-opsemRelevant to the opsem team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions