1
1
use arrayvec:: ArrayVec ;
2
2
use clippy_config:: Conf ;
3
- use clippy_utils:: diagnostics:: { span_lint_and_sugg, span_lint_and_then} ;
3
+ use clippy_utils:: diagnostics:: { span_lint , span_lint_and_sugg, span_lint_and_then} ;
4
4
use clippy_utils:: macros:: {
5
5
FormatArgsStorage , FormatParamUsage , MacroCall , find_format_arg_expr, format_arg_removal_span,
6
6
format_placeholder_format_span, is_assert_macro, is_format_macro, is_panic, matching_root_macro_call,
@@ -16,16 +16,18 @@ use rustc_ast::{
16
16
FormatPlaceholder , FormatTrait ,
17
17
} ;
18
18
use rustc_attr_parsing:: RustcVersion ;
19
- use rustc_data_structures:: fx:: FxHashMap ;
19
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
20
20
use rustc_errors:: Applicability ;
21
21
use rustc_errors:: SuggestionStyle :: { CompletelyHidden , ShowCode } ;
22
22
use rustc_hir:: { Expr , ExprKind , LangItem } ;
23
23
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
24
24
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment } ;
25
- use rustc_middle:: ty:: { List , Ty , TyCtxt } ;
25
+ use rustc_middle:: ty:: { self , GenericArg , List , TraitRef , Ty , TyCtxt , Upcast } ;
26
26
use rustc_session:: impl_lint_pass;
27
27
use rustc_span:: edition:: Edition :: Edition2021 ;
28
28
use rustc_span:: { Span , Symbol , sym} ;
29
+ use rustc_trait_selection:: infer:: TyCtxtInferExt ;
30
+ use rustc_trait_selection:: traits:: { Obligation , ObligationCause , Selection , SelectionContext } ;
29
31
30
32
declare_clippy_lint ! {
31
33
/// ### What it does
@@ -194,12 +196,34 @@ declare_clippy_lint! {
194
196
"use of a format specifier that has no effect"
195
197
}
196
198
199
+ declare_clippy_lint ! {
200
+ /// ### What it does
201
+ /// Detects [pointer format].
202
+ ///
203
+ /// ### Why restrict this?
204
+ /// In kernel context, this might be vulnerable to misuse for exfiltrating
205
+ /// stack or kernel function addresses.
206
+ ///
207
+ /// ### Example
208
+ /// ```no_run
209
+ /// let foo = &0_u32;
210
+ /// println!("{:p}", foo);
211
+ /// ```
212
+ ///
213
+ /// [pointer format]: https://doc.rust-lang.org/std/fmt/index.html#formatting-traits
214
+ #[ clippy:: version = "1.88.0" ]
215
+ pub POINTER_FORMAT ,
216
+ restriction,
217
+ "use of a pointer format specifier"
218
+ }
219
+
197
220
impl_lint_pass ! ( FormatArgs <' _> => [
198
221
FORMAT_IN_FORMAT_ARGS ,
199
222
TO_STRING_IN_FORMAT_ARGS ,
200
223
UNINLINED_FORMAT_ARGS ,
201
224
UNNECESSARY_DEBUG_FORMATTING ,
202
225
UNUSED_FORMAT_SPECS ,
226
+ POINTER_FORMAT ,
203
227
] ) ;
204
228
205
229
#[ allow( clippy:: struct_field_names) ]
@@ -279,6 +303,17 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> {
279
303
if placeholder. format_trait == FormatTrait :: Debug {
280
304
let name = self . cx . tcx . item_name ( self . macro_call . def_id ) ;
281
305
self . check_unnecessary_debug_formatting ( name, arg_expr) ;
306
+ if let Some ( span) = placeholder. span
307
+ && has_pointer_debug ( self . cx , self . cx . typeck_results ( ) . expr_ty ( arg_expr) )
308
+ {
309
+ span_lint ( self . cx , POINTER_FORMAT , span, "pointer formatting detected" ) ;
310
+ }
311
+ }
312
+
313
+ if placeholder. format_trait == FormatTrait :: Pointer
314
+ && let Some ( span) = placeholder. span
315
+ {
316
+ span_lint ( self . cx , POINTER_FORMAT , span, "pointer formatting detected" ) ;
282
317
}
283
318
}
284
319
}
@@ -595,3 +630,46 @@ where
595
630
}
596
631
}
597
632
}
633
+
634
+ fn has_pointer_debug < ' tcx > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> bool {
635
+ let mut visited = FxHashSet :: default ( ) ;
636
+ let mut queue = Vec :: new ( ) ;
637
+ queue. push ( ty) ;
638
+ while let Some ( ty) = queue. pop ( ) {
639
+ match ty. kind ( ) {
640
+ ty:: RawPtr ( _, _) => return true ,
641
+ ty:: Ref ( _, t, _) | ty:: Slice ( t) | ty:: Array ( t, _) => queue. push ( * t) ,
642
+ ty:: Tuple ( ts) => queue. extend_from_slice ( ts) ,
643
+ ty:: Adt ( adt, args) => {
644
+ // avoid infinite recursion
645
+ if !visited. insert ( adt. did ( ) ) {
646
+ continue ;
647
+ }
648
+ let tcx = cx. tcx ;
649
+ let Some ( trait_id) = tcx. get_diagnostic_item ( sym:: Debug ) else {
650
+ continue ;
651
+ } ;
652
+ let ( infcx, param_env) = tcx. infer_ctxt ( ) . build_with_typing_env ( cx. typing_env ( ) ) ;
653
+ let ty = tcx. erase_regions ( ty) ;
654
+ let trait_ref = TraitRef :: new ( tcx, trait_id, [ GenericArg :: from ( ty) ] ) ;
655
+ let obligation = Obligation {
656
+ cause : ObligationCause :: dummy ( ) ,
657
+ param_env,
658
+ recursion_depth : 0 ,
659
+ predicate : trait_ref. upcast ( tcx) ,
660
+ } ;
661
+ let selection = SelectionContext :: new ( & infcx) . select ( & obligation) ;
662
+ let Ok ( Some ( Selection :: UserDefined ( data) ) ) = selection else {
663
+ continue ;
664
+ } ;
665
+ // we currently only look into derived impls because those will
666
+ // debug-format the types fields which is easy enough to pull off
667
+ if tcx. has_attr ( data. impl_def_id , sym:: automatically_derived) {
668
+ queue. extend ( adt. all_fields ( ) . map ( |f| f. ty ( tcx, args) ) ) ;
669
+ }
670
+ } ,
671
+ _ => ( ) ,
672
+ }
673
+ }
674
+ false
675
+ }
0 commit comments