Skip to content

Miscompilation where binding only some fields leaks the others #90752

Closed

Description

@mk12

I found a bug where Rust leaks values (never calls drop). This is the root cause of a memory leak we saw in a Fuchsia test, caught by LeakSanitizer: https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=87961.

This is as far as I could minimize it:

#![allow(unused)]

struct S(u32);

impl Drop for S {
    fn drop(&mut self) {
        println!("dropping {}", self.0);
    }
}

enum E {
    A,
    B((S, S)),
}

fn main() {
    let mut foo = Some(E::A);
    match foo {
        Some(E::A) => (),
        _ => return,
    }
    foo.insert(E::B((S(1), S(2))));
    match foo {
        Some(E::B((x, _))) => {}
        _ => {}
    }
}

Expected output:

dropping 2
dropping 1

Actual output:

dropping 1

It leaked S(2). If you change (x, _) to (_, x) in the second match statement, it leaks S(1) instead. If you bind neither (_, _) or both (x, y), then both get dropped as expected.

If you use Box<u8> instead of S, you can detect a memory leak using LeakSanitizer or Valgrind.

Meta

I reproduced this on stable:

rustc 1.56.1 (59eed8a2a 2021-11-01)
binary: rustc
commit-hash: 59eed8a2aac0230a8b53e89d4e99d55912ba6b35
commit-date: 2021-11-01
host: x86_64-unknown-linux-gnu
release: 1.56.1
LLVM version: 13.0.0

And on nightly:

rustc 1.58.0-nightly (8b09ba6a5 2021-11-09)
binary: rustc
commit-hash: 8b09ba6a5d5c644fe0f1c27c7f9c80b334241707
commit-date: 2021-11-09
host: x86_64-unknown-linux-gnu
release: 1.58.0-nightly
LLVM version: 13.0.0

Activity

  1. added
    A-patternsRelating to patterns and pattern matching
    A-destructorsArea: Destructors (`Drop`, …)
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    I-compiler-nominatedNominated for discussion during a compiler team meeting.
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Nov 10, 2021
  2. jyn514 commented on Nov 10, 2021

    @jyn514
    Member

    Note that the miscompilation goes away if you change foo.insert((E::B) to foo = Some(E::B). So I suspect the unreachable_unchecked in insert is what's causing this somehow.

    unsafe { self.as_mut().unwrap_unchecked() }

  3. jyn514 commented on Nov 10, 2021

    @jyn514
    Member

    Also, the miscompilation is in MIR, not LLVM, because Miri also fails to run the destructor.

  4. jyn514 commented on Nov 10, 2021

    @jyn514
    Member

    Very slightly smaller (I removed the fields in S):

    struct S;
    
    impl Drop for S {
        fn drop(&mut self) {
            println!("dropping");
        }
    }
    
    enum E {
        A,
        B((S, S)),
    }
    
    fn main() {
        let mut foo = Some(E::A);
        match foo {
            Some(E::A) => (),
            Some(E::B(_)) | None => return,
        }
        foo.insert(E::B((S, S)));
        match foo {
            Some(E::B((x, _))) => {}
            _ => {},
        };
    }
  5. jyn514 commented on Nov 10, 2021

    @jyn514
    Member

    Oh strangely this is not related to the unwrap_unchecked, it still reproduces with a normal unwrap. https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=6e6a87916e94f4012b2d93615a7ce1d1

  6. jyn514 commented on Nov 10, 2021

    @jyn514
    Member

    Here's a version without std::option::Option (just an in-crate option): https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=025985867f32378dad0785597377a680

  7. nbdd0121 commented on Nov 10, 2021

    @nbdd0121
    Member

    A quick manual bisection on godbolt shows that this regresses in Rust 1.43 (is correct in Rust 1.42).

    @rustbot label: regression-from-stable-to-stable

  8. nbdd0121 commented on Nov 10, 2021

    @nbdd0121
    Member

    Bisection reveals that this issue is introduced in #68528.

    I think the issue is that precise_enum_drop_elaboration didn't re-mark all variants as possibly active after the mutable reference to the enum is used. The miscompilation goes away with -Z precise_enum_drop_elaboration=n. I filed #90756 to disable precise_enum_drop_elaboration by default, and we can turn this back on once the issue is fixed.

    cc @ecstatic-morse
    @rustbot label: +A-mir

  9. added
    A-MIRArea: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.html
    on Nov 10, 2021
  10. 13 remaining items

  11. added a commit that references this issue on Nov 13, 2021
  12. apiraino commented on Nov 18, 2021

    @apiraino
    Contributor

    Assigning priority as discussed in the Zulip thread of the Prioritization Working Group.

    @rustbot label -I-prioritize +P-critical

  13. added and removed
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Nov 18, 2021
  14. self-assigned this
    on Nov 18, 2021
  15. pnkfelix commented on Dec 2, 2021

    @pnkfelix
    Contributor

    FYI This is addressed by PR #90788, which will be part of the Rust 1.58 release (stable in six weeks).

  16. apiraino commented on Jan 20, 2022

    @apiraino
    Contributor

    Checking progress, @mk12 did you have a chance to test against the new stable 1.58 if it solves the issue? thanks

  17. mk12 commented on Jan 22, 2022

    @mk12
    ContributorAuthor

    @apiraino The example I gave in my first comment now behaves as expected on the Rust playground with 1.58.1 stable. I'm fairly sure this solves the original Fuchsia bug, but it's difficult to reproduce that because a workaround was used at the time and that code has changed a lot since.

  18. apiraino commented on Jan 27, 2022

    @apiraino
    Contributor

    I'm going to close this issue as we think it's solved. In case it can be reopened or follow-up issues can be created

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

Metadata

Metadata

Labels

A-MIRArea: Mid-level IR (MIR) - https://blog.rust-lang.org/2016/04/19/MIR.htmlA-destructorsArea: Destructors (`Drop`, …)A-patternsRelating to patterns and pattern matchingC-bugCategory: This is a bug.P-criticalCritical 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