Skip to content

binding-less matches on borrowed data are incorrectly allowed #45045

Description

@arielb1

This is actually an unsoundness in AST borrowck: AST borrowck doesn't check things that are matched on for conflicting borrows unless there are actually pattern bindings, e.g. this compiles:

enum Xyz {
    A,
    B,
}

fn main() {
    let mut e = Xyz::A;
    let f = &mut e;
    match e {
        Xyz::A => println!("a"),
        Xyz::B => println!("b"),
    };
    *f = Xyz::B;
}

That is unsound because of e.g. data races. For example, this code compiles and runs, and semi-reliably segfaults:

#![feature(test)]
use std::{thread, time};

extern crate crossbeam;
extern crate test;

enum Xyz<'a> {
    A(&'a usize),
    B(usize),
}

fn main() {
    let mut e = Xyz::A(&0);
    crossbeam::scope(|scope| {
        scope.spawn(|| {
            let now = time::Instant::now();
            let ten_millis = time::Duration::from_millis(50);
            while now.elapsed() < ten_millis {
                for _ in 0..1000000 {
                    e = Xyz::A(&0);
                    test::black_box(()); // compiler barrier
                    e = Xyz::B(0xaaaaaaaa);
                }
            }
        });
        let ten_millis = time::Duration::from_millis(10);
        thread::sleep(ten_millis);
        match e {
            Xyz::A(&0) => println!("a"),
            _ => println!("b"),
        };
    });
}

Activity

  1. added
    I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness
    on Oct 5, 2017
  2. bstrie commented on Oct 10, 2017

    @bstrie
    Contributor

    This is actually an unsoundness in AST borrowck

    Does this imply that this is another bug on the pile of things fixed by MIR borrowck?

  3. arielb1 commented on Oct 11, 2017

    @arielb1
    ContributorAuthor

    Does this imply that this is another bug on the pile of things fixed by MIR borrowck?

    Yeah.

  4. added this to the milestone on Jan 4, 2018
  5. nikomatsakis commented on Jan 4, 2018

    @nikomatsakis
    Contributor

    This still compiles with MIR borrowck, so I guess more work is needed.

  6. nikomatsakis commented on Jan 11, 2018

    @nikomatsakis
    Contributor

    Is this same problem as #27282 ?

  7. nikomatsakis commented on Jan 11, 2018

    @nikomatsakis
    Contributor

    It seems like the problem is that the discriminant statement is not considered an access. Here is the relevant portion of the MIR:

    bb0: {                              
            StorageLive(_1);                 // bb0[0]: scope 0 at src/main.rs:9:9: 9:14
            _1 = Xyz::A;                     // bb0[1]: scope 0 at src/main.rs:9:17: 9:23
            StorageLive(_2);                 // bb0[2]: scope 1 at src/main.rs:10:9: 10:10
            _2 = &mut _1;                    // bb0[3]: scope 1 at src/main.rs:10:13: 10:19
            _4 = discriminant(_1);           // bb0[4]: scope 3 at src/main.rs:12:9: 12:15
            switchInt(move _4) -> [0isize: bb1, 1isize: bb2, otherwise: bb3]; // bb0[5]: scope 3 at src/main.rs:12:9: 12:15
        }
    
  8. nikomatsakis commented on Jan 11, 2018

    @nikomatsakis
    Contributor

    @pnkfelix points out that two-phase borrows may be the cause. This example gets an error:

    #![feature(nll)]
    
    enum Xyz {
        A,
        B,
    }
    
    fn main() {
        let mut e = Xyz::A;
        let f = &mut e;
        let g = f;
        match e {
            Xyz::A => println!("a"),
            Xyz::B => println!("b"),
        };
        *g = Xyz::B;
    }

    this gets the following output:

    error[E0503]: cannot use `e` because it was mutably borrowed
      --> src/main.rs:13:9
       |
    10 |     let f = &mut e;
       |             ------ borrow of `e` occurs here
    ...
    13 |         Xyz::A => println!("a"),
       |         ^^^^^^ use of borrowed `e`
    
  9. nikomatsakis commented on Jan 11, 2018

    @nikomatsakis
    Contributor

    Copying @arielb1's segfaulting example and enabling nll mode also gives an error playpen

  10. arielb1 commented on Jan 13, 2018

    @arielb1
    ContributorAuthor

    @nikomatsakis

    That's indeed what I expect under 2-phase borrows.

  11. added a commit that references this issue on Jan 15, 2018
  12. added 2 commits that reference this issue on Jan 17, 2018
  13. modified the milestones: , on Jan 19, 2018
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-NLLArea: Non-lexical lifetimes (NLL)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/SoundnessP-mediumMedium priorityfixed-by-NLLBugs fixed, but only when NLL is enabled.

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions