Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segfault (use-after-free) in ty::lookup_field_type when pattern-matching structs #3215

Closed
bblum opened this issue Aug 17, 2012 · 10 comments
Closed
Labels
I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️

Comments

@bblum
Copy link
Contributor

bblum commented Aug 17, 2012

In very lucky situations (dependent on file size, file name, comments, compiler flags), when compiling a struct pattern, rustc will segfault outright. In all situations, it will use-after-free.

Run rustc under valgrind on this code:

// #1: "x: T" produces 3x invalid reads.
// #2: "x: ()" produces 1x invalid read (only the first one listed).
// #3: Making everything monomorphic at "()" is the same as #2.
struct Crash<T> { x: T; }

fn unwrap_crash<T>(+c: Crash<T>) {
    let Crash { x: _ } = c;
    // Exactly the same with: match c { Crash { x: _ } => { } }
    // If you write "Crash { _ }" it doesn't do any invalid reads
}

fn main() { } 

You should see:

==26425== Invalid read of size 8
==26425==    at 0x691AB35: middle::ty::subst
==26425==    by 0x68DEFE6: middle::ty::lookup_field_type
==26425==    by 0x6AAFA5E: middle::typeck::check::alt::check_pat
==26425==    by 0x6AA7188: _ZN6middle6typeck5check3alt9check_alt4anonE410
==26425==    by 0x6B42D96: middle::typeck::check::check_expr_with_unifier
==26425==  Address 0xa4ae580 is 32 bytes inside a block of size 56 free'd
==26425== 
==26425== Invalid read of size 8
==26425==    at 0x6A22C6A: middle::ty::subst::do_subst
==26425==    by 0x691AB62: middle::ty::subst
==26425==    by 0x68DEFE6: middle::ty::lookup_field_type
==26425==    by 0x6AAFA5E: middle::typeck::check::alt::check_pat
==26425==    by 0x6AA7188: _ZN6middle6typeck5check3alt9check_alt4anonE410
==26425==    by 0x6B42D96: middle::typeck::check::check_expr_with_unifier
==26425==  Address 0xa4ae580 is 32 bytes inside a block of size 56 free'd
==26425== 
==26425== Invalid read of size 8
==26425==    at 0x6A22C74: middle::ty::subst::do_subst
==26425==    by 0x691AB62: middle::ty::subst
==26425==    by 0x68DEFE6: middle::ty::lookup_field_type
==26425==    by 0x6AAFA5E: middle::typeck::check::alt::check_pat
==26425==    by 0x6AA7188: _ZN6middle6typeck5check3alt9check_alt4anonE410
==26425==    by 0x6B42D96: middle::typeck::check::check_expr_with_unifier
==26425==  Address 0xa4ae590 is 48 bytes inside a block of size 56 free'd
@bblum
Copy link
Contributor Author

bblum commented Aug 17, 2012

here is disassembly that shows where the first invalid read is: http://pastebin.mozilla.org/1760806
here is disassembly for the second and third: http://pastebin.mozilla.org/1760805

@bblum
Copy link
Contributor Author

bblum commented Aug 17, 2012

I wonder if we ought to back out any snapshots that have been taken since the new struct syntax was introduced, if that's really what it is. I wouldn't trust any code compiled with arbitrary values read from the heap.

@nikomatsakis
Copy link
Contributor

Do we have any idea what the bug is, really?

@eholk
Copy link
Contributor

eholk commented Aug 17, 2012

I had a use after free bug the other day that was related to moving out of an enum doing the wrong thing. I don't know if that would be at all related here though.

@bblum
Copy link
Contributor Author

bblum commented Aug 17, 2012

this reminds me of the use after free we ran into in icfp when using eric's unsafe move_it! macro. it could be that, or something related with move.

@eholk what was it? maybe rustc is doing the same thing.

@bblum
Copy link
Contributor Author

bblum commented Aug 17, 2012

The first use-after-free comes on the line if substs.tps.len() == 0u { in substs_is_noop. Somehow the argument substs: &substs to do_subst outlived its loan.

@bblum
Copy link
Contributor Author

bblum commented Aug 17, 2012

Found the root of it. In middle/typeck/check/alt.rs, we have:

237         let substitutions;
238         match structure_of(fcx, pat.span, expected) {
239             ty::ty_class(cid, ref substs) => {
241                 substitutions = substs;
243             }
251         }

And then later:

280         for fields.each |field| {
281             match field_map.find(field.ident) {
282                 some(index) => {
284                     let field_type = ty::lookup_field_type(tcx,
285                                                            class_id,
286                                                            class_field.id,
287                                                            substitutions);

This shouldn't have compiled. Or maybe it should have, and the ty::ty_class should have been forced to live longer?

@nikomatsakis
Copy link
Contributor

actually, I think this is the question that @graydon and I have been wrestling with. borrowck right now permits rvalues to live longer than you might expect---up to the surrounding fn or loop, but I don't think trans respects it (as I mentioned). The question is, who is wrong? I was going to go and patch up trans, but perhaps the bug is that borrowck is too liberal.

@bblum
Copy link
Contributor Author

bblum commented Aug 17, 2012

minimized test case - valgrind reports this also does a use-after-free:

fn main() {
    let msg;
    match some(~"Hello") {
        some(ref m) => {
            msg = m;
        },  
        none => { fail }
    }   
    io::println(*msg);
}

@bblum
Copy link
Contributor Author

bblum commented Aug 17, 2012

Opened #3217 so I can close this one with a workaround.

@bblum bblum closed this as completed Aug 17, 2012
@ghost ghost assigned bblum Aug 17, 2012
@bblum bblum removed their assignment Jun 16, 2014
flip1995 pushed a commit to flip1995/rust that referenced this issue Aug 11, 2020
…flip1995

Fix FP for `suspicious_arithmetic_impl` from `suspicious_trait_impl` …

As discussed in rust-lang#3215, the `suspicious_trait_impl` lint causes too many false positives, as it is complex to find out if binary operations are suspicious or not.

This PR restricts the number of binary operations to at most one, otherwise we don't lint.
This can be seen as very conservative, but at least FP can be reduced to bare minimum.

Fixes: rust-lang#3215

changelog: limit the `suspicious_arithmetic_impl` lint to one binop, to avoid many FPs
bors pushed a commit to rust-lang-ci/rust that referenced this issue May 15, 2021
allow to run a rustfmt command from cargo-fmt even when there is no target
RalfJung pushed a commit to RalfJung/rust that referenced this issue Dec 17, 2023
jaisnan pushed a commit to jaisnan/rust-dev that referenced this issue Jul 29, 2024
`lazy_cell` became stable:
rust-lang#121377.
`vtable_ptr` is renamed to `_vtable_ptr`:
rust-lang@d83f3ca8ca.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 and MIT licenses.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
I-crash Issue: The compiler crashes (SIGSEGV, SIGABRT, etc). Use I-ICE instead when the compiler panics. I-ICE Issue: The compiler panicked, giving an Internal Compilation Error (ICE) ❄️
Projects
None yet
Development

No branches or pull requests

3 participants