Skip to content

Borrow checker unsoundness with unions #45157

@petrochenkov

Description

@petrochenkov
Contributor
#![allow(unused)]

#[derive(Clone, Copy, Default)]
struct S {
    a: u8,
    b: u8,
}
#[derive(Clone, Copy, Default)]
struct Z {
    c: u8,
    d: u8,
}

union U {
    s: S,
    z: Z,
}

fn main() { unsafe {
    let mut u = U { s: Default::default() };
    
    let mref = &mut u.s.a;
    let err = &u.z.c; // This line compiles, but it certainly shouldn't ...
    drop(mref); // ... (at least if `mref` is used after `err`)
}}

"Cousins" of borrowed union sub-fields (and their further children) are not marked as borrowed.
The same bug should happen with move checking as well, but I haven't made an example yet.

Activity

added
I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness
on Oct 9, 2017
added
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
T-langRelevant to the language team
on Oct 13, 2017
nikomatsakis

nikomatsakis commented on Jan 18, 2018

@nikomatsakis
Contributor

This is still true in NLL, though I sort of thought we had tried to fix this.

nikomatsakis

nikomatsakis commented on Jan 18, 2018

@nikomatsakis
Contributor

I'm tagging this as WG-compiler-nll; I think it's something we should fix there.

joshtriplett

joshtriplett commented on Jan 18, 2018

@joshtriplett
Member

For the sake of potential testsuite additions: this also should not compile with a simultaneous borrow of u.s.a and u.z.d.

davidtwco

davidtwco commented on Jan 22, 2018

@davidtwco
Member

I'll take a look at this one.

nikomatsakis

nikomatsakis commented on Jan 22, 2018

@nikomatsakis
Contributor

I'm not really sure what the bug is here, but I can explain what I think should be happening. This function:

fn place_element_conflict(&self, elem1: &Place<'tcx>, elem2: &Place<'tcx>) -> Overlap {

is responsible for determining when two Place values overlap. I expect us to say that two fields from a union are always considered to potentially overlap. In this case, u.s and u.z would be considered to overlap. Specifically, comparing those two fields would return Arbitrary:

This is the code that I think should be responsible for doing that:

ty::TyAdt(def, _) if def.is_union() => {
// Different fields of a union, we are basically stuck.
debug!("place_element_conflict: STUCK-UNION");
Overlap::Arbitrary
}

It already looks a little fishy to me; in particular, it is returning Arbitrary if pi1 is a union, but I guess it should be equally true if pi2 is a union. Anyway, something here seems to be going awry!

31 remaining items

Loading
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-borrow-checkerArea: The borrow checkerC-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language teamfixed-by-NLLBugs fixed, but only when NLL is enabled.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Participants

      @nikomatsakis@joshtriplett@pnkfelix@Centril@bstrie

      Issue actions

        Borrow checker unsoundness with unions · Issue #45157 · rust-lang/rust