Skip to content

segfault on Rust 1.97.0 #159035

Closed
Closed

Description

@yonipeleg33

View all comments

I tried this code:

use std::hint::black_box;
enum Inner { A(u32), B(u32) }
struct Big { _pad: u64, inner: Inner }
struct Small { a: u16, b: u16, _f: fn() }
enum Checksum { X(Big), Y(Small) }
impl Checksum {
    fn finalize(self) -> u32 {
        match self {
            Checksum::X(h) => match h.inner { Inner::A(s) => s, Inner::B(s) => s },
            Checksum::Y(s) => (u32::from(s.b) << 16) | u32::from(s.a),
        }
    }
}
#[inline(never)]
fn run(c: Option<Checksum>) -> Option<u32> {
    c.map(|c| c.finalize())
}
fn main() { println!("{:?}", run(black_box(None))); }

Command:
rustup run <toolchain> rustc -O minimal.rs -o minimal && ./minimal
With toolchain being either 1.97.0 or 1.96.1.

I expected to see this happen: Exit code 0, None printed

Instead, this happened: [1] 112371 segmentation fault (core dumped) ./minimal

Meta

rustc --version --verbose:

rustc --version --verbose
rustc 1.97.0 (2d8144b78 2026-07-07)
binary: rustc
commit-hash: 2d8144b7880597b6e6d3dfd63a9a9efae3f533d3
commit-date: 2026-07-07
host: x86_64-unknown-linux-gnu
release: 1.97.0
LLVM version: 22.1.6

I checked beta (1.98.0) and nightly (1.99.0) - same result

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Jul 9, 2026
  2. added
    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.
    on Jul 9, 2026
  3. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    I-miscompileIssue: Correct Rust code lowers to incorrect machine code
    on Jul 9, 2026
  4. added
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Jul 9, 2026
  5. removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Jul 9, 2026
  6. matthiaskrgr commented on Jul 9, 2026

    @matthiaskrgr
    Member

    regression in #155941 it seems

  7. matthiaskrgr commented on Jul 9, 2026

    @matthiaskrgr
    Member

    the rollup is missing some artifacts but cbr points to #155850 cc @scottmcm , still would take it with a grain of salt

  8. scottmcm commented on Jul 9, 2026

    @scottmcm
    Member

    If #155850 does prove the be the issue feel free to revert -- it's tiny and bool-like things aren't the critical part of #155473 anyway.

  9. scottmcm commented on Jul 9, 2026

    @scottmcm
    Member

    Oh, the LLVM IR diff that we emit (pre-optimization) definitely makes this look like #155850 : https://rust.godbolt.org/z/KoE4Me95h

      %0 = load i64, ptr %c, align 8
    - %1 = icmp eq i64 %0, 2
    + %1 = icmp eq i64 %0, -1
      %_2 = select i1 %1, i64 0, i64 1
      %2 = trunc nuw i64 %_2 to i1

    That's exactly the kind of change that PR makes: Checksum has a 0-or-1 tag, then Option<Checksum>::None changed from 2 to -1.

    Of course, it's still selected back to 0-or-1 for the rest of the code (that deals in discriminants not tags to use), so it doesn't look wrong...

  10. asquared31415 commented on Jul 9, 2026

    @asquared31415
    Contributor

    In MIR there's a IntSwitch on the discriminant that assumes that the discriminant value is 0 or 1 for Option::<Checksum> and is unreachable for any other values. Is that potentially an issue, or is this discriminant different than the discriminant in codegen? or maybe it is different and that's the problem?

  11. scottmcm commented on Jul 9, 2026

    @scottmcm
    Member

    Huh, LLVM is really clever on this code https://rust.godbolt.org/z/8ofrzaWbc

    1.96.0:

    example::run::hc386b3a8fe279f46:
            mov     rcx, qword ptr [rdi]
            xor     eax, eax
            cmp     rcx, 2
            setne   al
            mov     edx, dword ptr [rdi + 4*rcx + 12]
            ret

    Nightly:

    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

    If I'm following that right, it's directly using the stored tag field in the addressing mode to load the correct data.

    But in nightly when a None (tag_field = -1) is passed that mov ecx, ecx takes the -1_i64 in rcx and turns it into 0x00000000_FFFFFFFF_i64 in rcx, which then results in reading out of bounds when a None is passed.

    I think I'm over my head in spelunking here. Perhaps someone from @rust-lang/wg-llvm would have a better idea whether this is us emitting something wrong or LLVM somehow handling this wrong? Near as I could tell we're only doing the trunc nuw to i1 in the branch where the option is non-None, and thus it's fine, but I could absolutely be missing something.

  12. asquared31415 commented on Jul 9, 2026

    @asquared31415
    Contributor

    Edit: wait this doesn't happen if we set -C no-prepopulate-passes, does that mean that it's an LLVM pass that's producing the unconditional trunc nuw?

    %1 = trunc nuw i64 %0 to i1 is wrong.

    If the nuw keyword is present, and any of the truncated bits are non-zero, the result is a poison value.

    %0 is potentially 0b1111111..111, which definitely means that a lot of non-zero bits were truncated.

  13. scottmcm commented on Jul 9, 2026

    @scottmcm
    Member

    @asquared31415 The Discriminant rvalue in MIR is always the discriminant -- 0_isize or 1_isize for Option -- no matter how that's actually stored. That's critical for generic MIR working at all, since how None is stored is of course completely different for different generic parameters.

    That's why after the icmp eq in #159035 (comment) it expands the i1 comparison result via select …, i64 0, i64 1: it's turning the stored tag (0/1 for Some, 2 for None) into the discriminant (0_isize for None, 1_isize for Some).

    (You'll notice that both the old and new MIR just switch over zero or one for the option, neither handling -1 nor 2.)

  14. 48 remaining items

  15. added a commit that references this issue on Jul 25, 2026
  16. added a commit that references this issue on Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.C-bugCategory: This is a bug.I-miscompileIssue: Correct Rust code lowers to incorrect machine codeO-x86_64Target: x86-64 processors (like x86_64-*) (also known as amd64 and x64)P-criticalCritical priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.llvm-fixed-upstreamIssue expected to be fixed by the next major LLVM upgrade, or backported fixesregression-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