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 @ 💬).

Activity

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

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions