Skip to content

Commit bb275cc

Browse files
authored
Add a lint for the Self tuple struct soundness hole (#318)
1 parent 39be052 commit bb275cc

10 files changed

+141
-0
lines changed

doc/src/config-lints.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,3 +264,26 @@ foo::<dyn SomeTrait>();
264264
// Trait object in type default (enum, union, trait, and so on are all also forbidden)
265265
struct SomeStruct<T = dyn SomeTrait>(...);
266266
```
267+
268+
### `plrust_tuple_struct_self_pattern`
269+
270+
This lint forbids use of a tuple struct named `Self` in pattern position. This
271+
can be used to bypass struct field privacy prior to Rust 1.71.0
272+
(<https://github.com/rust-lang/rust/issues/111220>). Once PL/Rust depends on
273+
1.71.0, this lint will be replaced by one that does nothing, as the offending
274+
pattern will not compile.
275+
276+
For example, this lint will prevent the following code:
277+
278+
```rs
279+
mod my {
280+
pub struct Foo(&'static str);
281+
}
282+
283+
impl AsRef<str> for my::Foo {
284+
fn as_ref(&self) -> &str {
285+
let Self(s) = self;
286+
s
287+
}
288+
}
289+
```

plrust/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ const DEFAULT_LINTS: &'static str = "\
9595
plrust_print_macros, \
9696
plrust_stdio, \
9797
plrust_suspicious_trait_object, \
98+
plrust_tuple_struct_self_pattern, \
9899
unsafe_code, \
99100
deprecated, \
100101
suspicious_auto_trait_impls, \

plrustc/plrustc/src/lints/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ mod print_macros;
1919
mod static_impls;
2020
mod stdio;
2121
mod sus_trait_object;
22+
mod tuple_struct_self_pattern;
2223

2324
static INCLUDE_TEST_ONLY_LINTS: Lazy<bool> =
2425
Lazy::new(|| std::env::var("PLRUSTC_INCLUDE_TEST_ONLY_LINTS").is_ok());
@@ -38,6 +39,7 @@ static PLRUST_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
3839
print_macros::PLRUST_PRINT_MACROS,
3940
stdio::PLRUST_STDIO,
4041
sus_trait_object::PLRUST_SUSPICIOUS_TRAIT_OBJECT,
42+
tuple_struct_self_pattern::PLRUST_TUPLE_STRUCT_SELF_PATTERN,
4143
];
4244
if *INCLUDE_TEST_ONLY_LINTS {
4345
let test_only_lints = [force_ice::PLRUST_TEST_ONLY_FORCE_ICE];
@@ -83,6 +85,7 @@ pub fn register(store: &mut LintStore, _sess: &Session) {
8385
store.register_late_pass(move |_| Box::new(stdio::PlrustPrintFunctions));
8486
store.register_late_pass(move |_| Box::new(extern_blocks::NoExternBlockPass));
8587
store.register_late_pass(move |_| Box::new(lifetime_param_trait::LifetimeParamTraitPass));
88+
store.register_late_pass(move |_| Box::new(tuple_struct_self_pattern::TupleStructSelfPat));
8689

8790
if *INCLUDE_TEST_ONLY_LINTS {
8891
store.register_early_pass(move || Box::new(force_ice::PlrustcForceIce));
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Turn this into a no-op once 1.71.0 hits stable
2+
// (https://github.com/rust-lang/rust/issues/111220)
3+
use hir::def::Res;
4+
use rustc_hir as hir;
5+
use rustc_lint::{LateContext, LateLintPass, LintContext};
6+
use rustc_middle::ty;
7+
8+
declare_plrust_lint!(
9+
pub(crate) PLRUST_TUPLE_STRUCT_SELF_PATTERN,
10+
"`Self` patterns for tuple structs",
11+
);
12+
13+
rustc_lint_defs::declare_lint_pass!(TupleStructSelfPat => [PLRUST_TUPLE_STRUCT_SELF_PATTERN]);
14+
15+
impl<'tcx> LateLintPass<'tcx> for TupleStructSelfPat {
16+
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) {
17+
let hir::PatKind::TupleStruct(hir::QPath::Resolved(_, path), ..) = pat.kind else {
18+
return;
19+
};
20+
let Res::SelfCtor(ctor_did) = path.res else {
21+
return;
22+
};
23+
let o: Option<ty::TraitRef> = cx.tcx.impl_trait_ref(ctor_did);
24+
let Some(trait_ref) = o else {
25+
return;
26+
};
27+
let self_ty = trait_ref.self_ty();
28+
let ty::Adt(adt_def, _) = self_ty.kind() else {
29+
return;
30+
};
31+
let Some(ctor) = adt_def.non_enum_variant().ctor_def_id() else {
32+
return;
33+
};
34+
if !cx
35+
.tcx
36+
.visibility(ctor)
37+
.is_accessible_from(cx.tcx.parent_module(pat.hir_id).to_def_id(), cx.tcx)
38+
{
39+
cx.lint(
40+
PLRUST_TUPLE_STRUCT_SELF_PATTERN,
41+
"`Self` pattern on tuple struct used to access private field",
42+
|b| b.set_span(pat.span),
43+
);
44+
}
45+
}
46+
}

plrustc/plrustc/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ extern crate rustc_interface;
77

88
extern crate rustc_lint;
99
extern crate rustc_lint_defs;
10+
extern crate rustc_middle;
1011
extern crate rustc_session;
1112
extern crate rustc_span;
1213

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#![crate_type = "lib"]
2+
3+
use core::ptr::NonNull;
4+
5+
trait Bad {
6+
fn bad(&mut self);
7+
}
8+
impl<T> Bad for Box<T> {
9+
fn bad(&mut self) {
10+
let Self(ptr, _) = self;
11+
12+
fn dangling<T, U>() -> U
13+
where
14+
U: From<NonNull<T>>,
15+
{
16+
NonNull::dangling().into()
17+
}
18+
19+
*ptr = dangling();
20+
}
21+
}
22+
23+
fn main() {
24+
let mut foo = Box::new(123);
25+
foo.bad();
26+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: `Self` pattern on tuple struct used to access private field
2+
--> $DIR/tuple_struct_self_pat_box.rs:10:13
3+
|
4+
LL | let Self(ptr, _) = self;
5+
| ^^^^^^^^^^^^
6+
|
7+
= note: `-F plrust-tuple-struct-self-pattern` implied by `-F plrust-lints`
8+
9+
error: aborting due to previous error
10+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![crate_type = "lib"]
2+
3+
mod my {
4+
pub struct Foo(&'static str);
5+
}
6+
impl AsRef<str> for my::Foo {
7+
fn as_ref(&self) -> &str {
8+
let Self(s) = self;
9+
s
10+
}
11+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: `Self` pattern on tuple struct used to access private field
2+
--> $DIR/tuple_struct_self_pat_local_priv.rs:8:13
3+
|
4+
LL | let Self(s) = self;
5+
| ^^^^^^^
6+
|
7+
= note: `-F plrust-tuple-struct-self-pattern` implied by `-F plrust-lints`
8+
9+
error: aborting due to previous error
10+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![crate_type = "lib"]
2+
3+
pub struct Foo(&'static str);
4+
5+
impl AsRef<str> for Foo {
6+
fn as_ref(&self) -> &str {
7+
let Self(s) = self;
8+
s
9+
}
10+
}

0 commit comments

Comments
 (0)