diff --git a/doc/src/config-lints.md b/doc/src/config-lints.md index 7fd7cc67..24d33f5c 100644 --- a/doc/src/config-lints.md +++ b/doc/src/config-lints.md @@ -230,3 +230,14 @@ impl std::marker::Unpin for Foo {} ``` As a workaround, in most cases, you should be able to use [`std::panic::AssertUnwindSafe`](https://doc.rust-lang.org/nightly/std/panic/struct.AssertUnwindSafe.html) instead of implementing one of the `UnwindSafe` traits, and Boxing your type can usually work around the need for `Unpin` (which should be rare in non-`async` code anyway). + +### `plrust_suspicious_trait_object` + +This lint forbids trait object use in turbofish and generic defaults. This is an effort to fix [certain soundness holes](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=764d78856996e1985ee88819b013c645) in the Rust language. More simply, the following patterns are disallowed: + +```rs +// Trait object in turbofish +foo::(); +// Trait object in type default (enum, union, trait, and so on are all also forbidden) +struct SomeStruct(...); +``` diff --git a/plrust/src/lib.rs b/plrust/src/lib.rs index 4380c153..fb28f3d6 100644 --- a/plrust/src/lib.rs +++ b/plrust/src/lib.rs @@ -78,6 +78,7 @@ const DEFAULT_LINTS: &'static str = "\ plrust_external_mod, \ plrust_print_macros, \ plrust_stdio, \ + plrust_suspicious_trait_object, \ unsafe_code, \ deprecated, \ suspicious_auto_trait_impls, \ diff --git a/plrustc/plrustc/src/lints.rs b/plrustc/plrustc/src/lints.rs index 43fa6794..265d110b 100644 --- a/plrustc/plrustc/src/lints.rs +++ b/plrustc/plrustc/src/lints.rs @@ -107,6 +107,65 @@ impl<'tcx> LateLintPass<'tcx> for PlrustAutoTraitImpls { } } +declare_plrust_lint!( + pub(crate) PLRUST_SUSPICIOUS_TRAIT_OBJECT, + "Disallow suspicious generic use of trait objects", +); + +declare_lint_pass!(PlrustSuspiciousTraitObject => [PLRUST_SUSPICIOUS_TRAIT_OBJECT]); + +impl<'tcx> LateLintPass<'tcx> for PlrustSuspiciousTraitObject { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr) { + let path_segments = match &expr.kind { + hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => path.segments, + hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment, ..)) + | hir::ExprKind::MethodCall(segment, ..) => std::slice::from_ref(*segment), + _ => return, + }; + for segment in path_segments { + let Some(args) = segment.args else { + continue; + }; + for arg in args.args { + let hir::GenericArg::Type(ty) = arg else { + continue; + }; + if let hir::TyKind::TraitObject(..) = &ty.kind { + cx.lint( + PLRUST_SUSPICIOUS_TRAIT_OBJECT, + "trait objects in turbofish are forbidden", + |b| b.set_span(expr.span), + ); + } + } + } + } + + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + let generics = match &item.kind { + hir::ItemKind::TyAlias(_, generics) => *generics, + hir::ItemKind::Enum(_, generics) => *generics, + hir::ItemKind::Struct(_, generics) => *generics, + hir::ItemKind::Union(_, generics) => *generics, + hir::ItemKind::Trait(_, _, generics, ..) => *generics, + hir::ItemKind::Fn(_, generics, ..) => *generics, + _ => return, + }; + for param in generics.params { + let hir::GenericParamKind::Type { default: Some(ty), .. } = ¶m.kind else { + continue; + }; + if let hir::TyKind::TraitObject(..) = &ty.kind { + cx.lint( + PLRUST_SUSPICIOUS_TRAIT_OBJECT, + "trait objects in generic defaults are forbidden", + |b| b.set_span(item.span), + ); + } + } + } +} + declare_plrust_lint!( pub(crate) PLRUST_LIFETIME_PARAMETERIZED_TRAITS, "Disallow lifetime parameterized traits" @@ -544,6 +603,7 @@ static PLRUST_LINTS: Lazy> = Lazy::new(|| { PLRUST_LIFETIME_PARAMETERIZED_TRAITS, PLRUST_PRINT_MACROS, PLRUST_STDIO, + PLRUST_SUSPICIOUS_TRAIT_OBJECT, ]; if *INCLUDE_TEST_ONLY_LINTS { let test_only_lints = [PLRUST_TEST_ONLY_FORCE_ICE]; @@ -579,6 +639,7 @@ pub fn register(store: &mut LintStore, _sess: &Session) { ); store.register_early_pass(move || Box::new(PlrustAsync)); store.register_early_pass(move || Box::new(PlrustExternalMod)); + store.register_late_pass(move |_| Box::new(PlrustSuspiciousTraitObject)); store.register_late_pass(move |_| Box::new(PlrustAutoTraitImpls)); store.register_late_pass(move |_| Box::new(PlrustFnPointer)); store.register_late_pass(move |_| Box::new(PlrustLeaky)); diff --git a/plrustc/plrustc/uitests/sus_trait_obj_items.rs b/plrustc/plrustc/uitests/sus_trait_obj_items.rs new file mode 100644 index 00000000..f4693812 --- /dev/null +++ b/plrustc/plrustc/uitests/sus_trait_obj_items.rs @@ -0,0 +1,34 @@ +#![crate_type = "lib"] + +pub trait Foo {} + +pub trait Bar +where + T: ?Sized, +{ +} + +#[allow(invalid_type_param_default)] // not the lint we're interested in testing +pub fn sus_fn() +where + T: ?Sized, +{ +} + +pub struct SusStruct(pub Box) +where + T: ?Sized; + +pub enum SusEnum +where + T: ?Sized, +{ + Something(Box), +} + +pub union SusUnion +where + T: ?Sized, +{ + pub something: *const T, +} diff --git a/plrustc/plrustc/uitests/sus_trait_obj_items.stderr b/plrustc/plrustc/uitests/sus_trait_obj_items.stderr new file mode 100644 index 00000000..75bb4069 --- /dev/null +++ b/plrustc/plrustc/uitests/sus_trait_obj_items.stderr @@ -0,0 +1,54 @@ +error: trait objects in generic defaults are forbidden + --> $DIR/sus_trait_obj_items.rs:5:1 + | +LL | / pub trait Bar +LL | | where +LL | | T: ?Sized, +LL | | { +LL | | } + | |_^ + | + = note: `-F plrust-suspicious-trait-object` implied by `-F plrust-lints` + +error: trait objects in generic defaults are forbidden + --> $DIR/sus_trait_obj_items.rs:12:1 + | +LL | / pub fn sus_fn() +LL | | where +LL | | T: ?Sized, +LL | | { +LL | | } + | |_^ + +error: trait objects in generic defaults are forbidden + --> $DIR/sus_trait_obj_items.rs:18:1 + | +LL | / pub struct SusStruct(pub Box) +LL | | where +LL | | T: ?Sized; + | |______________^ + +error: trait objects in generic defaults are forbidden + --> $DIR/sus_trait_obj_items.rs:22:1 + | +LL | / pub enum SusEnum +LL | | where +LL | | T: ?Sized, +LL | | { +LL | | Something(Box), +LL | | } + | |_^ + +error: trait objects in generic defaults are forbidden + --> $DIR/sus_trait_obj_items.rs:29:1 + | +LL | / pub union SusUnion +LL | | where +LL | | T: ?Sized, +LL | | { +LL | | pub something: *const T, +LL | | } + | |_^ + +error: aborting due to 5 previous errors + diff --git a/plrustc/plrustc/uitests/sus_trait_obj_transmute.rs b/plrustc/plrustc/uitests/sus_trait_obj_transmute.rs new file mode 100644 index 00000000..22ddd3ed --- /dev/null +++ b/plrustc/plrustc/uitests/sus_trait_obj_transmute.rs @@ -0,0 +1,17 @@ +#![crate_type = "lib"] + +trait Object { + type Output; +} + +impl Object for T { + type Output = U; +} + +fn foo(x: >::Output) -> U { + x +} + +pub fn transmute(x: T) -> U { + foo::, U>(x) +} diff --git a/plrustc/plrustc/uitests/sus_trait_obj_transmute.stderr b/plrustc/plrustc/uitests/sus_trait_obj_transmute.stderr new file mode 100644 index 00000000..64a078c8 --- /dev/null +++ b/plrustc/plrustc/uitests/sus_trait_obj_transmute.stderr @@ -0,0 +1,10 @@ +error: trait objects in turbofish are forbidden + --> $DIR/sus_trait_obj_transmute.rs:16:5 + | +LL | foo::, U>(x) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-F plrust-suspicious-trait-object` implied by `-F plrust-lints` + +error: aborting due to previous error +