Skip to content

Commit 22d3431

Browse files
committed
Validate use of parameters in naked functions
* Reject use of parameters inside naked function body. * Reject use of patterns inside function parameters, to emphasize role of parameters a signature declaration (mirroring existing behaviour for function declarations) and avoid generating code introducing specified bindings.
1 parent 773ddba commit 22d3431

File tree

7 files changed

+221
-49
lines changed

7 files changed

+221
-49
lines changed

Diff for: compiler/rustc_interface/src/passes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,7 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> {
816816
let local_def_id = tcx.hir().local_def_id(module);
817817
tcx.ensure().check_mod_loops(local_def_id);
818818
tcx.ensure().check_mod_attrs(local_def_id);
819+
tcx.ensure().check_mod_naked_functions(local_def_id);
819820
tcx.ensure().check_mod_unstable_api_usage(local_def_id);
820821
tcx.ensure().check_mod_const_bodies(local_def_id);
821822
});

Diff for: compiler/rustc_middle/src/query/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,10 @@ rustc_queries! {
635635
desc { |tcx| "checking loops in {}", describe_as_module(key, tcx) }
636636
}
637637

638+
query check_mod_naked_functions(key: LocalDefId) -> () {
639+
desc { |tcx| "checking naked functions in {}", describe_as_module(key, tcx) }
640+
}
641+
638642
query check_mod_item_types(key: LocalDefId) -> () {
639643
desc { |tcx| "checking item types in {}", describe_as_module(key, tcx) }
640644
}

Diff for: compiler/rustc_passes/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
88
#![feature(const_fn)]
99
#![feature(const_panic)]
10+
#![feature(crate_visibility_modifier)]
1011
#![feature(in_band_lifetimes)]
1112
#![feature(nll)]
1213
#![feature(or_patterns)]
@@ -32,6 +33,7 @@ pub mod layout_test;
3233
mod lib_features;
3334
mod liveness;
3435
pub mod loops;
36+
mod naked_functions;
3537
mod reachable;
3638
mod region;
3739
pub mod stability;
@@ -46,6 +48,7 @@ pub fn provide(providers: &mut Providers) {
4648
lang_items::provide(providers);
4749
lib_features::provide(providers);
4850
loops::provide(providers);
51+
naked_functions::provide(providers);
4952
liveness::provide(providers);
5053
intrinsicck::provide(providers);
5154
reachable::provide(providers);

Diff for: compiler/rustc_passes/src/naked_functions.rs

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use rustc_hir as hir;
2+
use rustc_hir::def_id::LocalDefId;
3+
use rustc_hir::intravisit::{ErasedMap, FnKind, NestedVisitorMap, Visitor};
4+
use rustc_middle::ty::query::Providers;
5+
use rustc_middle::ty::TyCtxt;
6+
use rustc_span::symbol::sym;
7+
use rustc_span::Span;
8+
9+
fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
10+
tcx.hir().visit_item_likes_in_module(
11+
module_def_id,
12+
&mut CheckNakedFunctions { tcx }.as_deep_visitor(),
13+
);
14+
}
15+
16+
crate fn provide(providers: &mut Providers) {
17+
*providers = Providers { check_mod_naked_functions, ..*providers };
18+
}
19+
20+
struct CheckNakedFunctions<'tcx> {
21+
tcx: TyCtxt<'tcx>,
22+
}
23+
24+
impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> {
25+
type Map = ErasedMap<'tcx>;
26+
27+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
28+
NestedVisitorMap::None
29+
}
30+
31+
fn visit_fn(
32+
&mut self,
33+
fk: FnKind<'v>,
34+
_fd: &'tcx hir::FnDecl<'tcx>,
35+
body_id: hir::BodyId,
36+
_span: Span,
37+
_hir_id: hir::HirId,
38+
) {
39+
match fk {
40+
// Rejected during attribute check. Do not validate further.
41+
FnKind::Closure(..) => return,
42+
FnKind::ItemFn(..) | FnKind::Method(..) => {}
43+
}
44+
45+
let naked = fk.attrs().iter().any(|attr| attr.has_name(sym::naked));
46+
if naked {
47+
let body = self.tcx.hir().body(body_id);
48+
check_params(self.tcx, body);
49+
check_body(self.tcx, body);
50+
}
51+
}
52+
}
53+
54+
/// Checks that parameters don't use patterns. Mirrors the checks for function declarations.
55+
fn check_params(tcx: TyCtxt<'_>, body: &hir::Body<'_>) {
56+
for param in body.params {
57+
match param.pat.kind {
58+
hir::PatKind::Wild
59+
| hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, _, None) => {}
60+
_ => {
61+
tcx.sess
62+
.struct_span_err(
63+
param.pat.span,
64+
"patterns not allowed in naked function parameters",
65+
)
66+
.emit();
67+
}
68+
}
69+
}
70+
}
71+
72+
/// Checks that function parameters aren't referenced in the function body.
73+
fn check_body<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>) {
74+
let mut params = hir::HirIdSet::default();
75+
for param in body.params {
76+
param.pat.each_binding(|_binding_mode, hir_id, _span, _ident| {
77+
params.insert(hir_id);
78+
});
79+
}
80+
CheckBody { tcx, params }.visit_body(body);
81+
}
82+
83+
struct CheckBody<'tcx> {
84+
tcx: TyCtxt<'tcx>,
85+
params: hir::HirIdSet,
86+
}
87+
88+
impl<'tcx> Visitor<'tcx> for CheckBody<'tcx> {
89+
type Map = ErasedMap<'tcx>;
90+
91+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
92+
NestedVisitorMap::None
93+
}
94+
95+
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
96+
if let hir::ExprKind::Path(hir::QPath::Resolved(
97+
_,
98+
hir::Path { res: hir::def::Res::Local(var_hir_id), .. },
99+
)) = expr.kind
100+
{
101+
if self.params.contains(var_hir_id) {
102+
self.tcx
103+
.sess
104+
.struct_span_err(
105+
expr.span,
106+
"use of parameters not allowed inside naked functions",
107+
)
108+
.emit();
109+
}
110+
}
111+
hir::intravisit::walk_expr(self, expr);
112+
}
113+
}

Diff for: src/test/codegen/naked-functions.rs

+5-49
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// compile-flags: -C no-prepopulate-passes -Zmir-opt-level=0
1+
// compile-flags: -C no-prepopulate-passes
22

33
#![crate_type = "lib"]
44
#![feature(naked_functions)]
@@ -15,11 +15,9 @@ pub fn naked_empty() {
1515
// CHECK: Function Attrs: naked
1616
#[no_mangle]
1717
#[naked]
18-
// CHECK-NEXT: define void @naked_with_args(i{{[0-9]+( %0)?}})
18+
// CHECK-NEXT: define void @naked_with_args(i{{[0-9]+( %a)?}})
1919
pub fn naked_with_args(a: isize) {
2020
// CHECK-NEXT: {{.+}}:
21-
// CHECK-NEXT: %a = alloca i{{[0-9]+}}
22-
&a; // keep variable in an alloca
2321
// CHECK: ret void
2422
}
2523

@@ -34,53 +32,11 @@ pub fn naked_with_return() -> isize {
3432
}
3533

3634
// CHECK: Function Attrs: naked
37-
// CHECK-NEXT: define i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+( %0)?}})
35+
// CHECK-NEXT: define i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+( %a)?}})
3836
#[no_mangle]
3937
#[naked]
4038
pub fn naked_with_args_and_return(a: isize) -> isize {
4139
// CHECK-NEXT: {{.+}}:
42-
// CHECK-NEXT: %a = alloca i{{[0-9]+}}
43-
&a; // keep variable in an alloca
44-
// CHECK: ret i{{[0-9]+}} %{{[0-9]+}}
45-
a
46-
}
47-
48-
// CHECK: Function Attrs: naked
49-
// CHECK-NEXT: define void @naked_recursive()
50-
#[no_mangle]
51-
#[naked]
52-
pub fn naked_recursive() {
53-
// CHECK-NEXT: {{.+}}:
54-
// CHECK-NEXT: call void @naked_empty()
55-
56-
// FIXME(#39685) Avoid one block per call.
57-
// CHECK-NEXT: br label %bb1
58-
// CHECK: bb1:
59-
60-
naked_empty();
61-
62-
// CHECK-NEXT: %_4 = call i{{[0-9]+}} @naked_with_return()
63-
64-
// FIXME(#39685) Avoid one block per call.
65-
// CHECK-NEXT: br label %bb2
66-
// CHECK: bb2:
67-
68-
// CHECK-NEXT: %_3 = call i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+}} %_4)
69-
70-
// FIXME(#39685) Avoid one block per call.
71-
// CHECK-NEXT: br label %bb3
72-
// CHECK: bb3:
73-
74-
// CHECK-NEXT: call void @naked_with_args(i{{[0-9]+}} %_3)
75-
76-
// FIXME(#39685) Avoid one block per call.
77-
// CHECK-NEXT: br label %bb4
78-
// CHECK: bb4:
79-
80-
naked_with_args(
81-
naked_with_args_and_return(
82-
naked_with_return()
83-
)
84-
);
85-
// CHECK-NEXT: ret void
40+
// CHECK: ret i{{[0-9]+}} 0
41+
0
8642
}

Diff for: src/test/ui/asm/naked-params.rs

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Check that use of function parameters is validate in naked functions.
2+
//
3+
// ignore-wasm32 asm unsupported
4+
#![feature(asm)]
5+
#![feature(naked_functions)]
6+
#![feature(or_patterns)]
7+
#![crate_type = "lib"]
8+
9+
#[repr(C)]
10+
pub struct P { x: u8, y: u16 }
11+
12+
#[naked]
13+
pub unsafe extern "C" fn f(
14+
mut a: u32,
15+
//~^ ERROR patterns not allowed in naked function parameters
16+
&b: &i32,
17+
//~^ ERROR patterns not allowed in naked function parameters
18+
(None | Some(_)): Option<std::ptr::NonNull<u8>>,
19+
//~^ ERROR patterns not allowed in naked function parameters
20+
P { x, y }: P,
21+
//~^ ERROR patterns not allowed in naked function parameters
22+
) {
23+
asm!("", options(noreturn))
24+
}
25+
26+
#[naked]
27+
pub unsafe extern "C" fn inc(a: u32) -> u32 {
28+
a + 1
29+
//~^ ERROR use of parameters not allowed inside naked functions
30+
}
31+
32+
#[naked]
33+
pub unsafe extern "C" fn inc_asm(a: u32) -> u32 {
34+
asm!("/* {0} */", in(reg) a, options(noreturn));
35+
//~^ ERROR use of parameters not allowed inside naked functions
36+
}
37+
38+
#[naked]
39+
pub unsafe extern "C" fn sum(x: u32, y: u32) -> u32 {
40+
// FIXME: Should be detected by asm-only check.
41+
(|| { x + y})()
42+
}
43+
44+
pub fn outer(x: u32) -> extern "C" fn(usize) -> usize {
45+
#[naked]
46+
pub extern "C" fn inner(y: usize) -> usize {
47+
*&y
48+
//~^ ERROR use of parameters not allowed inside naked functions
49+
}
50+
inner
51+
}

Diff for: src/test/ui/asm/naked-params.stderr

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
error: patterns not allowed in naked function parameters
2+
--> $DIR/naked-params.rs:14:5
3+
|
4+
LL | mut a: u32,
5+
| ^^^^^
6+
7+
error: patterns not allowed in naked function parameters
8+
--> $DIR/naked-params.rs:16:5
9+
|
10+
LL | &b: &i32,
11+
| ^^
12+
13+
error: patterns not allowed in naked function parameters
14+
--> $DIR/naked-params.rs:18:6
15+
|
16+
LL | (None | Some(_)): Option<std::ptr::NonNull<u8>>,
17+
| ^^^^^^^^^^^^^^
18+
19+
error: patterns not allowed in naked function parameters
20+
--> $DIR/naked-params.rs:20:5
21+
|
22+
LL | P { x, y }: P,
23+
| ^^^^^^^^^^
24+
25+
error: use of parameters not allowed inside naked functions
26+
--> $DIR/naked-params.rs:28:5
27+
|
28+
LL | a + 1
29+
| ^
30+
31+
error: use of parameters not allowed inside naked functions
32+
--> $DIR/naked-params.rs:34:31
33+
|
34+
LL | asm!("/* {0} */", in(reg) a, options(noreturn));
35+
| ^
36+
37+
error: use of parameters not allowed inside naked functions
38+
--> $DIR/naked-params.rs:47:11
39+
|
40+
LL | *&y
41+
| ^
42+
43+
error: aborting due to 7 previous errors
44+

0 commit comments

Comments
 (0)