Skip to content

Commit b86fb56

Browse files
committed
new restriction lint: pointer_format
1 parent b363499 commit b86fb56

File tree

5 files changed

+173
-3
lines changed

5 files changed

+173
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6162,6 +6162,7 @@ Released 2018-09-13
61626162
[`pathbuf_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#pathbuf_init_then_push
61636163
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
61646164
[`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false
6165+
[`pointer_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointer_format
61656166
[`pointers_in_nomem_asm_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointers_in_nomem_asm_block
61666167
[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
61676168
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
165165
crate::floating_point_arithmetic::SUBOPTIMAL_FLOPS_INFO,
166166
crate::format::USELESS_FORMAT_INFO,
167167
crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO,
168+
crate::format_args::POINTER_FORMAT_INFO,
168169
crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO,
169170
crate::format_args::UNINLINED_FORMAT_ARGS_INFO,
170171
crate::format_args::UNNECESSARY_DEBUG_FORMATTING_INFO,

clippy_lints/src/format_args.rs

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use arrayvec::ArrayVec;
22
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};
44
use clippy_utils::macros::{
55
FormatArgsStorage, FormatParamUsage, MacroCall, find_format_arg_expr, format_arg_removal_span,
66
format_placeholder_format_span, is_assert_macro, is_format_macro, is_panic, matching_root_macro_call,
@@ -16,16 +16,18 @@ use rustc_ast::{
1616
FormatPlaceholder, FormatTrait,
1717
};
1818
use rustc_attr_parsing::RustcVersion;
19-
use rustc_data_structures::fx::FxHashMap;
19+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2020
use rustc_errors::Applicability;
2121
use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode};
2222
use rustc_hir::{Expr, ExprKind, LangItem};
2323
use rustc_lint::{LateContext, LateLintPass, LintContext};
2424
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};
2626
use rustc_session::impl_lint_pass;
2727
use rustc_span::edition::Edition::Edition2021;
2828
use rustc_span::{Span, Symbol, sym};
29+
use rustc_trait_selection::infer::TyCtxtInferExt;
30+
use rustc_trait_selection::traits::{Obligation, ObligationCause, Selection, SelectionContext};
2931

3032
declare_clippy_lint! {
3133
/// ### What it does
@@ -194,12 +196,34 @@ declare_clippy_lint! {
194196
"use of a format specifier that has no effect"
195197
}
196198

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+
197220
impl_lint_pass!(FormatArgs<'_> => [
198221
FORMAT_IN_FORMAT_ARGS,
199222
TO_STRING_IN_FORMAT_ARGS,
200223
UNINLINED_FORMAT_ARGS,
201224
UNNECESSARY_DEBUG_FORMATTING,
202225
UNUSED_FORMAT_SPECS,
226+
POINTER_FORMAT,
203227
]);
204228

205229
#[allow(clippy::struct_field_names)]
@@ -279,6 +303,17 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> {
279303
if placeholder.format_trait == FormatTrait::Debug {
280304
let name = self.cx.tcx.item_name(self.macro_call.def_id);
281305
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");
282317
}
283318
}
284319
}
@@ -595,3 +630,46 @@ where
595630
}
596631
}
597632
}
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+
}

tests/ui/pointer_format.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#![warn(clippy::pointer_format)]
2+
3+
#[derive(Debug)]
4+
struct ContainsPointerDeep {
5+
w: WithPointer,
6+
}
7+
8+
struct ManualDebug {
9+
ptr: *const u8,
10+
}
11+
12+
#[derive(Debug)]
13+
struct WithPointer {
14+
len: usize,
15+
ptr: *const u8,
16+
}
17+
18+
impl std::fmt::Debug for ManualDebug {
19+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
20+
f.write_str("ManualDebug")
21+
}
22+
}
23+
24+
fn main() {
25+
let m = &(main as fn());
26+
let g = &0;
27+
let o = &format!("{m:p}");
28+
//~^ pointer_format
29+
println!("{g:p}");
30+
//~^ pointer_format
31+
panic!("{o:p}");
32+
//~^ pointer_format
33+
let answer = 42;
34+
let x = &raw const answer;
35+
let arr = [0u8; 8];
36+
let with_ptr = WithPointer { len: 8, ptr: &arr as _ };
37+
let _ = format!("{x:?}");
38+
//~^ pointer_format
39+
print!("{with_ptr:?}");
40+
//~^ pointer_format
41+
let container = ContainsPointerDeep { w: with_ptr };
42+
print!("{container:?}");
43+
//~^ pointer_format
44+
45+
let no_pointer = "foo";
46+
println!("{no_pointer:?}");
47+
let manual_debug = ManualDebug { ptr: &arr as _ };
48+
println!("{manual_debug:?}");
49+
}

tests/ui/pointer_format.stderr

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
error: pointer formatting detected
2+
--> tests/ui/pointer_format.rs:27:23
3+
|
4+
LL | let o = &format!("{m:p}");
5+
| ^^^^^
6+
|
7+
= note: `-D clippy::pointer-format` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::pointer_format)]`
9+
10+
error: pointer formatting detected
11+
--> tests/ui/pointer_format.rs:29:15
12+
|
13+
LL | println!("{g:p}");
14+
| ^^^^^
15+
16+
error: pointer formatting detected
17+
--> tests/ui/pointer_format.rs:31:13
18+
|
19+
LL | panic!("{o:p}");
20+
| ^^^^^
21+
22+
error: pointer formatting detected
23+
--> tests/ui/pointer_format.rs:37:22
24+
|
25+
LL | let _ = format!("{x:?}");
26+
| ^^^^^
27+
28+
error: pointer formatting detected
29+
--> tests/ui/pointer_format.rs:39:13
30+
|
31+
LL | print!("{with_ptr:?}");
32+
| ^^^^^^^^^^^^
33+
34+
error: pointer formatting detected
35+
--> tests/ui/pointer_format.rs:42:13
36+
|
37+
LL | print!("{container:?}");
38+
| ^^^^^^^^^^^^^
39+
40+
error: aborting due to 6 previous errors
41+

0 commit comments

Comments
 (0)