Skip to content

Miscompilation from .wrapping_offset(isize::MIN).wrapping_offset(isize::MIN) #112526

Closed
@cbeuw

Description

@cbeuw

Again, fuzzer generated code and minimised to surface Rust. Miri reports no UB under either aliasing model.

extern crate core;
use core::ptr;

pub fn dump_var(val0: u16) {
    println!("{val0}");
}

// extern "C" {
//     fn dump_var(x: u16);
// }
// #[no_mangle]
pub unsafe fn fn10_rs() -> *const *mut i64 {
    let mut _16: usize = 0;
    let mut _23: i128 = 0;
    let mut isize_min: isize = 0;
    let mut _46: i64 = 0;
    let mut _107: i16 = 0;
    let mut _47: u16 = 0;
    let mut _88: usize = 0;

    let mut _31: *mut u8 = ptr::null_mut();
    let mut _33: *mut *mut i64 = ptr::null_mut();
    let mut _81: *const i128 = ptr::null();
    let mut _90: *mut u8 = ptr::null_mut();
    let mut _177: *mut i64 = ptr::null_mut();

    let mut _44: ((u64, u64, u8), u32, f32) = Default::default();
    let mut _61: ((u64, u64, u8), u32, f32) = Default::default();
    let mut tup: (((u64, u64, u8), u32, f32), bool) = Default::default();
    let mut _95: (((u64, u64, u8), u32, f32), bool) = Default::default();

    'bb2: loop {
        let two: u16 = 2;
        _44.0 .2 = 1;
        _31 = core::ptr::addr_of_mut!(_44.0 .2);
        _23 = 11;
        'bb45: loop {
            (*_31) = 1;
            isize_min = isize::MIN;
            'bb65: loop {
                let tup_ptr = core::ptr::addr_of_mut!(tup);
                _31 = core::ptr::addr_of_mut!((*tup_ptr).0 .0 .2);
                _16 = 18215089233857363959_usize;
                match isize_min {
                    isize::MIN => {
                        _90 = _31.wrapping_offset(isize::MIN);
                        _95.0 = _44;
                        _46 = 42;
                        _81 = core::ptr::addr_of!(_23);
                        _44 = tup.0;
                        _88 = _16;
                        'bb80: loop {
                            _31 = _90.wrapping_offset(isize_min);
                            match *_81 {
                                11 => 'bb88: loop {
                                    let tup_ptr2 = core::ptr::addr_of_mut!(tup);
                                    (*tup_ptr2) = _95;
                                    isize_min = _107 as isize;
                                    _47 = two >> *_31;
                                    (*tup_ptr) = _95;
                                    match _88 {
                                        18215089233857363959 => {
                                            *_31 = _61.0 .2.swap_bytes();
                                            _88 = (*tup_ptr2).0 .2 as usize;
                                            _33 = core::ptr::addr_of_mut!(_177);
                                            match _46 {
                                                42 => {
                                                    (*_33) = core::ptr::addr_of_mut!(_46);
                                                    match *_177 {
                                                        42 => {
                                                            dump_var(_47);
                                                            return core::ptr::addr_of!(_177);
                                                        }
                                                        _ => continue 'bb2,
                                                    }
                                                }
                                                _ => match *_81 {
                                                    11 => continue 'bb88,
                                                    _ => continue 'bb65,
                                                },
                                            }
                                        }
                                        0 => continue 'bb80,
                                        _ => continue 'bb65,
                                    }
                                },
                                _ => continue 'bb65,
                            }
                        }
                    }
                    _ => continue 'bb45,
                }
            }
        }
    }
}

pub fn main() {
    unsafe {
        fn10_rs();
    }
}

The correct output is 1, evaluated from two >> *_31 where _31 points to 1 after having been roundtripped with two .wrapping_offset(isize::MIN).

It outputs 2 with -Copt-level >= 1.

% rustc -Copt-level=0 repro.rs && ./repro
1
% rustc -Copt-level=1 repro.rs && ./repro
2

Not sure if rustc is emitting LLVM IR with UB or it's a bug in LLVM. llvm-reduce gave me this which calls dump_var(2) with opt -O1: https://godbolt.org/z/q6GWPq9qs, but the GEP indices don't look right.

target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
target triple = "arm64-apple-macosx11.0.0"

define ptr @fn10_rs() {
start:
  %_17 = alloca { { { i64, i64, i8, [7 x i8] }, i32, float }, i8, [7 x i8] }, align 8
  %tup = alloca { { { i64, i64, i8, [7 x i8] }, i32, float }, i8, [7 x i8] }, align 8
  %0 = getelementptr { i64, i64, i8, [7 x i8] }, ptr %tup, i64 -384307168202282325, i32 1
  %1 = getelementptr i8, ptr %0, i64 -9223372036854775808
  call void @llvm.memcpy.p0.p0.i64(ptr %tup, ptr %_17, i64 40, i1 false)
  %_18 = load i8, ptr %1, align 8
  %2 = zext i8 %_18 to i16
  %_47 = lshr i16 2, %2
  call void @dump_var(i16 %_47)
  ret ptr null
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0

declare void @dump_var(i16)

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

cc @RalfJung @nikic

Metadata

Metadata

Assignees

Labels

A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.A-raw-pointersArea: raw pointers, MaybeUninit, NonNullA-rustlantisA miscompilation found by RustlantisI-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-mediumMedium priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions