Skip to content

Variable debuginfo introduces undefined behaviour #105386

Closed
@tmiasko

Description

@tmiasko

When generating a variable debuginfo, rustc emits an expression describing the address of a source variable. Since #83941, rustc also evaluates this expression.

The expression can contain indirections, and it is not always valid to evaluate it. Consider a source variable that is described indirectly in terms of a base LocalRef::Place local. The source variable will be introduced at the beginning of a function, but at this point the base local might be uninitialized. For example:

#![feature(stmt_expr_attributes)]
pub struct S([usize; 8]);

#[no_mangle]
pub fn f(x: S, y: S) -> usize {
    (#[inline(always)]|| {
        let _z = x;
        y.0[0]
    })()
}
$ rustc --crate-type=lib c.rs -O -Cdebuginfo=2 -Cno-prepopulate-passes -Zunpretty=mir
...
fn f(_1: S, _2: S) -> usize {
    debug x => _1;                       // in scope 0 at c.rs:5:10: 5:11
    debug y => _2;                       // in scope 0 at c.rs:5:16: 5:17
    let mut _0: usize;                   // return place in scope 0 at c.rs:5:25: 5:30
    let mut _3: [closure@c.rs:6:23: 6:25]; // in scope 0 at c.rs:6:5: 9:7
    let mut _4: &S;                      // in scope 0 at c.rs:6:5: 9:7
    scope 1 (inlined f::{closure#0}) {   // at c.rs:6:5: 9:9
        debug x => (_3.0: S);            // in scope 1 at c.rs:5:10: 5:11
        debug y => (*(_3.1: &S));        // in scope 1 at c.rs:5:16: 5:17
...
$ rustc --crate-type=lib c.rs -O -Cdebuginfo=2 -Cno-prepopulate-passes --emit llvm-ir
...
%S = type { [8 x i64] }
%"[closure@c.rs:6:23: 6:25]" = type { %S, ptr }

; Function Attrs: nonlazybind uwtable
define i64 @f(ptr noalias nocapture noundef readonly dereferenceable(64) %x, ptr noalias nocapture noundef readonly dereferenceable(64) %y) unnamed_addr #0 !dbg !6 {
start:
  %_z = alloca %S, align 8
  %_3 = alloca %"[closure@c.rs:6:23: 6:25]", align 8
  call void @llvm.dbg.declare(metadata ptr %x, metadata !21, metadata !DIExpression()), !dbg !23
  call void @llvm.dbg.declare(metadata ptr %y, metadata !22, metadata !DIExpression()), !dbg !24
  call void @llvm.dbg.declare(metadata ptr %_3, metadata !25, metadata !DIExpression()), !dbg !39
  %0 = getelementptr inbounds %"[closure@c.rs:6:23: 6:25]", ptr %_3, i32 0, i32 1
  %1 = load ptr, ptr %0, align 8, !nonnull !19, !align !41, !noundef !19
  call void @llvm.dbg.declare(metadata ptr %_3, metadata !36, metadata !DIExpression(DW_OP_plus_uconst, 64, DW_OP_deref)), !dbg !42
  call void @llvm.dbg.declare(metadata ptr %_z, metadata !37, metadata !DIExpression()), !dbg !43
  call void @llvm.lifetime.start.p0(i64 72, ptr %_3), !dbg !40

Note that %1 = load has !nonnull and !noundef metadata but loaded value is uninitialized.

(This leads to end-to-end miscompilation with -Cdebuginfo=2).

cc @wesleywiser, @nagisa #83941

Metadata

Metadata

Assignees

Labels

A-debuginfoArea: Debugging information in compiled programs (DWARF, PDB, etc.)A-mir-opt-inliningArea: MIR inliningC-bugCategory: This is a bug.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions