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

rustc_typeck::check::compare_method::compare_const_impl doesn't drain its fullfillment context nor invoke regionck. #41323

Closed
eddyb opened this issue Apr 15, 2017 · 7 comments
Labels
A-associated-items Area: Associated items (types, constants & functions) A-typesystem Area: The type system E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@eddyb
Copy link
Member

eddyb commented Apr 15, 2017

UPDATE: Mentoring instructions for fixing this bug can be found here.


There are some calls to add obligations to a fulfillment context but it's never checked.

Some examples of code that shouldn't compile:

trait TransmuteTarget: Sized {
    type Out: Sized;
}

// Can't use generic impl here.
impl TransmuteTarget for String {
    type Out = String;
}

trait Transmute: TransmuteTarget {
    const FROM: Self::Out;
}

impl<T: TransmuteTarget> Transmute for T {
    // Associated type projections aren't checked (obligation fulfillment).
    const FROM: &'static str = "foo";
}
trait Foo {
    const NAME: &'static str;
}

impl<'a> Foo for &'a () {
    // Lifetimes aren't checked (regionck).
    const NAME: &'a str = "unit";
}

cc @rust-lang/compiler

@eddyb eddyb added A-associated-items Area: Associated items (types, constants & functions) A-typesystem Area: The type system I-nominated T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 15, 2017
@nikomatsakis
Copy link
Contributor

triage: P-medium

@rust-highfive rust-highfive added P-medium Medium priority and removed I-nominated labels Apr 20, 2017
@withoutboats
Copy link
Contributor

@eddyb does this block stabilization of associated consts?

@eddyb
Copy link
Member Author

eddyb commented May 22, 2017

@withoutboats Yes, as it allows const types in impls that don't match the trait definition.

@withoutboats
Copy link
Contributor

withoutboats commented May 22, 2017

This can be exploited to elongate lifetimes:

#![feature(associated_consts)]

trait Evil<'a, T: 'static> {
    const EVIL: fn(&'a T) -> &'static T;
}

impl<'a, T: 'static> Evil<'a, T> for () {
    const EVIL: fn(&'a T) -> &'a T = identity;
}

fn identity<T: 'static>(s: &T) -> &T { s }

fn evil() -> &'static Box<i32> {
    let b = Box::new(0);
    <()>::EVIL(&b)
}


fn main() {
    println!("{}", **evil())
}

@withoutboats withoutboats added the I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness label May 22, 2017
@eddyb eddyb changed the title rustc_typeck::check::compare_method::compare_const_impl doesn't use its fullfillment context. rustc_typeck::check::compare_method::compare_const_impl doesn't drain its fullfillment context nor invoke regionck. May 22, 2017
@aturon
Copy link
Member

aturon commented May 22, 2017

I'm renominating, given the exploit that @withoutboats found.

@nikomatsakis
Copy link
Contributor

This is a fairly straight-forward fix, so I'm going to write up some mentoring instructions. Maybe somebody wants to jump in on it!

In general, the code in compare_method.rs is used to compare an associated item definition found in an impl against the trait specification. The name of the file is somewhat outdated; used to be that methods were the only thing where we needed special checks.

I actually suspect that the code for methods and constants could be combined into one function (as this comment suggests), but we can leave that for bonus points.

Let's walk through what the code does now. Imagine we have something like impl<T> Foo<T> for Vec<T> { const X: U = .... Our goal is to check that this type U matches what it says in the trait.

  • First, we obtain the "trait reference" that the trait is implemented (Foo<T> for Vec<T>, in our example) here and extract its "substs" -- these are the values for the generic paramters of the trait ([Vec<T>, T], as the first thing is the Self parameter).
  • Next, we load the type of the associated item from the impl (U) here
  • Then we load the type from the trait here and apply the substitutions from the trait reference. This will substitute Vec<T> for Self and T for the trait's other type parameter, basically.
  • We then have to normalize those types (remove any associated types we can). You always have to do this after loading types from the HIR currently. Here.
  • Next we insist that these types are subtypes here and report an error if we get back an error result.

This is where things go wrong. First off, I suspect it'd be better to just require that the types are equal, rather than subtypes. That can be done by changing from sub to eq. But more importantly, the subtype operation works like this:

  • If it gives back an Error, you get an error, that case is fine.
  • If it gives back Ok, this does not mean you are done:
    • It also gives back a series of obligations that you must go and "prove" -- these might be trait requirements or other things. We are processing those obligations by "registering" them with the 'fulfillment context' here, which is good, but we're missing a step later.
    • Furthermore, there may be various region requirements setup that are being overlooked.

Basically, we've enqueued up a bunch of further checks that we ought to do, but nowhere are we doing them.

We ought to be adding two things (here I am giving links into the method code):

  • "Selecting" all the obligations that we registered in the fulfillment context. This will check that all the trait obligations are met and so forth. The method code does that here -- this basically means calling select_all_or_error(&infcx).
  • Performing the "region check" -- the method code does that here. This will do various region checks and then ensure that the region inference can succeed. I think this step is the one that is needed to prevent the example that @withoutboats gave.

In both cases, we should be able to essentially cut-and-paste the existing code.

@nikomatsakis
Copy link
Contributor

Tagging as E-mentor.

@nikomatsakis nikomatsakis added E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. and removed I-nominated labels May 26, 2017
seanmonstar added a commit to seanmonstar/rust that referenced this issue May 31, 2017
frewsxcv added a commit to frewsxcv/rust that referenced this issue Jun 1, 2017
…l, r=nikomatsakis

associated_consts: check trait obligations and regionck for associated consts

Closes rust-lang#41323

r? @nikomatsakis
frewsxcv added a commit to frewsxcv/rust that referenced this issue Jun 1, 2017
…l, r=nikomatsakis

associated_consts: check trait obligations and regionck for associated consts

Closes rust-lang#41323

r? @nikomatsakis
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-associated-items Area: Associated items (types, constants & functions) A-typesystem Area: The type system E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

5 participants