Skip to content

[x86] Incorrect load combining causing OOB reads (and thus segfaults at runtime) #208611

Description

@scottmcm

Context: Rust issue in today's 1.97 release, rust-lang/rust#159035
Rust repro with opt pipeline, https://rust.godbolt.org/z/dTPGY3rY1

Take this LLVM IR, particularly noting that %0 can be -1, 0, or 1 as conveyed by the !range attribute

define { i32, i32 } @"example[aafd2ff9cfc6cff8]::run"(ptr dead_on_return noalias nofree noundef readonly align 8 captures(none) dereferenceable(24) %c) unnamed_addr {
start:
  %0 = load i64, ptr %c, align 8, !range !4, !noundef !5
  %.not = icmp ne i64 %0, -1
  %_3.sroa.5.0.c.sroa_idx = getelementptr inbounds nuw i8, ptr %c, i64 12
  %_3.sroa.5.0.copyload = load i32, ptr %_3.sroa.5.0.c.sroa_idx, align 4
  %1 = trunc nuw i64 %0 to i1
  %2 = getelementptr inbounds nuw i8, ptr %c, i64 16
  %_3.sroa.6.0.copyload = load i32, ptr %2, align 8
  %_4.sroa.0.1 = select i1 %1, i32 %_3.sroa.6.0.copyload, i32 %_3.sroa.5.0.copyload
  %_0.sroa.3.0 = select i1 %.not, i32 %_4.sroa.0.1, i32 undef
  %_0.sroa.0.0 = zext i1 %.not to i32
  %3 = insertvalue { i32, i32 } poison, i32 %_0.sroa.0.0, 0
  %4 = insertvalue { i32, i32 } %3, i32 %_0.sroa.3.0, 1
  ret { i32, i32 } %4
}

!4 = !{i64 -1, i64 2}
!5 = !{}

Today on llc trunk that compiles to the following: https://llvm.godbolt.org/z/TcY99vhjE

"example[aafd2ff9cfc6cff8]::run":
        mov     rcx, qword ptr [rdi]
        xor     eax, eax
        cmp     rcx, -1
        setne   al
        mov     ecx, ecx
        mov     edx, dword ptr [rdi + 4*rcx + 12]
        ret

rcx there maps to %0, so it can be -1, 0, or 1 (as i64).

If it stayed that way, that would be fine, as the [rdi + 4*rcx + 12] would read 4 bytes from rdi + 8, rdi + 12, or rdi + 16, all of which are within the dereferenceable(24).

However, that mov ecx, ecx changes rcx to instead be 0x00000000FFFFFFFF, 0, or 1 (as i64), and thus the following mov reads massively out of bounds, causing a segfault.


Apologies in advance if I got the logic chain wrong here somewhere. I'm reasonably good at middle-end IR but worse at assembly and completely unfamiliar with backend IR.

I think this is x86-specific because it was reported that neither aarch64 nor riscv64gc hit a problem here (Rust Zulip #t-compiler > Miscompilation in 1.97.0 with new Option::None discriminant @ 💬).

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions