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

target_feature_11 allows bypassing safety checks through Fn* traits #72012

Closed
hanna-kruppe opened this issue May 8, 2020 · 12 comments · Fixed by #73306
Closed

target_feature_11 allows bypassing safety checks through Fn* traits #72012

hanna-kruppe opened this issue May 8, 2020 · 12 comments · Fixed by #73306
Labels
A-target-feature Area: Enabling/disabling target features like AVX, Neon, etc. C-bug Category: This is a bug. F-target_feature_11 target feature 1.1 RFC I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@hanna-kruppe
Copy link
Contributor

(Moved from #69098 (comment) with an added PoC.)

The following program (playground) demonstrates how current implementation of target_feature_11 allows using a target_feature from safe code without ensuring it's actually available:

#![feature(target_feature_11)]

#[target_feature(enable="avx")]
fn use_avx() {
    println!("Hello from AVX")
}

fn call_it(f: impl FnOnce()) {
    f();
}

fn main() {
    call_it(use_avx);
}

This is unsound because it allows executing (e.g.) AVX instructions on CPUs that do not implement them, which is UB. It only works because "safe fns with target_features" are erroneously considered to implement the FnOnce/FnMut/Fn traits.

@hanna-kruppe hanna-kruppe added I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness C-bug Category: This is a bug. requires-nightly This issue requires a nightly compiler in some way. labels May 8, 2020
@petrochenkov
Copy link
Contributor

petrochenkov commented May 8, 2020

Proposed resolution: #69098 (comment).

It would probably be better to add the unsafe to #[target_feature] functions implicitly during lowering to HIR.
Implicit unsafe is already added to functions in extern blocks in the same way.

That would make the unsafety checker the only place (besides AST lowering) where they would be treated specially. Like, "yes, the function is unsafe, but we know that it's safe to call in this specific context, so the unsafe block can be omitted".

The special coercion checks would no longer be necessary in that case.

@nikomatsakis nikomatsakis added the F-target_feature_11 target feature 1.1 RFC label May 8, 2020
@jonas-schievink jonas-schievink added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. F-target_feature_11 target feature 1.1 RFC and removed F-target_feature_11 target feature 1.1 RFC labels May 8, 2020
@petrochenkov
Copy link
Contributor

Or perhaps the RFC should be revised to still require writing unsafe fn and only unsafety checker need to be enhanced to allow calling unsafe functions without unsafe blocks in the situations known to be safe.

That would be even less magic, which is good, IMO.
The ergonomic regression would be minor because unsafe would still be not required at call sites.

@nikomatsakis
Copy link
Contributor

nikomatsakis commented May 8, 2020

Good catch. Another option is that referencing, not calling, a target-feature function is unsafe, but I guess that is a bit inconsistent given that this already compiles (playground):

#[target_feature(enable="avx")]
unsafe fn use_avx() {
    println!("Hello from AVX")
}

fn call_it(f: impl FnOnce()) {
    f();
}

fn main() {
    let x = use_avx;
}

In other words, accessing a safe target feature 1.1 function would be unsafe (unless the target feature is in scope), not just calling it.

I think that this is what I would expect somehow, but I would expect it to apply uniformly to both safe and unsafe target feature functions (and that's not an option).

Or perhaps the RFC should be revised to still require writing unsafe fn and only unsafety checker need to be enhanced to allow calling unsafe functions without unsafe blocks in the situations known to be safe.

The downside of this approach is that we don't know why the function was declared unsafe, not really -- was it only because of the target feature? Or were there additional requirements that would have made it unsafe?

@hanna-kruppe
Copy link
Contributor Author

The downside of this approach is that we don't know why the function was declared unsafe, not really -- was it only because of the target feature? Or were there additional requirements that would have made it unsafe?

For example, a whole bunch of core::arch::* functions take raw pointers and use them to access memory, so they need to be unsafe even if the respective target feature is enabled in the calling context.

@petrochenkov
Copy link
Contributor

The downside of this approach is that we don't know why the function was declared unsafe, not really -- was it only because of the target feature? Or were there additional requirements that would have made it unsafe?

Ah, right, that's a good argument.
The "unsafety checker enhancement" should only affect unsafe functions that are made unsafe through #[target_feature], but not other means (explicit unsafe or extern block).

@Elinvynia Elinvynia added the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Jun 9, 2020
@LeSeulArtichaut LeSeulArtichaut removed the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Jun 9, 2020
@calebzulawski
Copy link
Member

I'm new to the compiler, but it seemed non-trivial to change #[target_feature] functions to unsafe when lowering to HIR. It looks to me like you'd end up with just as many exceptions (ignoring the unsafe in some scenarios), in addition tracking the origin of the unsafe. On the other hand it was pretty trivial to not implement the Fn traits instead. I'll open a PR with my fix, but please close it if the other method is preferred.

@nikomatsakis
Copy link
Contributor

nikomatsakis commented Jun 22, 2020

So there are sort of two questions here.

One of them is what user-visible behavior is permitted, and the other is how precisely that is implemented in the compiler. I think the latter only concerns the @rust-lang/lang team in-so-far as it represents the impact on the language spec and on the "mental model" that users must keep in mind.

Nominating for the lang-team meeting to discuss @calebzulawski's solution, which is simply to make "target-feature" functions not implement the Fn traits (just as unsafe functions do not implement the Fn traits, along with functions with non-Rust ABIs etc).

@LeSeulArtichaut LeSeulArtichaut added the T-lang Relevant to the language team, which will review and decide on the PR/issue. label Jun 22, 2020
@calebcartwright

This comment has been minimized.

@nikomatsakis

This comment has been minimized.

@nikomatsakis
Copy link
Contributor

We discussed this in the @rust-lang/lang meeting today. The conclusion was that the fix that @calebzulawski proposed is good for right now. It's already the case that many fn items do not implement the function trait (e.g., those with custom ABIs or which are unsafe), so this would be "yet another" case (admittedly a somewhat different one in that this case doesn't show up in the type of function pointers, for better or worse).

We uncovered a few other interesting observations:

@joshtriplett pointed out that their ideal preference would be to have something like "use_avx only implements FnOnce" within an unsafe block, but of course the trait system has no precedent for that. It would be sort of like a where unsafe attached to the impl block (i.e., the impl is usable in some context where unsafe code is allowed).

Another possibility might be "use_avx only implements FnOnce within a fn that has a suitable target feature in scope". This is an interesting idea because, although the pointer could escape in the form of a dyn FnOnce, the idea is that once you enter use_avx, you've already demonstrated that you possess the target feature, so it's ok if the value escapes. (This would be like a where target_feature(avx) clause.)

To that end, I was thinking that you could model this last point using closures, but I found that it doesn't work. In particular, || use_avx() gives an error, even if it appears inside of a function annotated with #[target_feature]. This seems like something we should discuss, but it's a separate issue.

We did discuss briefly whether target features should be part of the type of function pointers but I think the conclusion was 'well maybe but there is already stable code out there that lets you coerce, and it seems rather niche'. We also discussed whether we should permit one to annotate closures with target-feature annotations.

In any case, those are things we can address separately from this issue, but the point is that adopting more accepting solutions can be done in the future.

@nikomatsakis
Copy link
Contributor

I'm going to move the question of whether closures should "inherit" target features to the main tracking issue -- I'm not sure if that is something that was discussed as part of the original RFC discussion?

@nikomatsakis
Copy link
Contributor

Opened #73631 to discuss closures.

Manishearth added a commit to Manishearth/rust that referenced this issue Jul 1, 2020
…trait-soundness, r=nikomatsakis

Don't implement Fn* traits for #[target_feature] functions

Closes rust-lang#72012.
Manishearth added a commit to Manishearth/rust that referenced this issue Jul 2, 2020
…trait-soundness, r=nikomatsakis

Don't implement Fn* traits for #[target_feature] functions

Closes rust-lang#72012.
Manishearth added a commit to Manishearth/rust that referenced this issue Jul 2, 2020
…trait-soundness, r=nikomatsakis

Don't implement Fn* traits for #[target_feature] functions

Closes rust-lang#72012.
Manishearth added a commit to Manishearth/rust that referenced this issue Jul 2, 2020
…trait-soundness, r=nikomatsakis

Don't implement Fn* traits for #[target_feature] functions

Closes rust-lang#72012.
@bors bors closed this as completed in 3d391d2 Jul 2, 2020
@workingjubilee workingjubilee added the A-target-feature Area: Enabling/disabling target features like AVX, Neon, etc. label Mar 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-target-feature Area: Enabling/disabling target features like AVX, Neon, etc. C-bug Category: This is a bug. F-target_feature_11 target feature 1.1 RFC I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants