]>
Commit | Line | Data |
---|---|---|
29967ef6 XL |
1 | use crate::LateContext; |
2 | use crate::LateLintPass; | |
3 | use crate::LintContext; | |
064997fb | 4 | use rustc_errors::fluent; |
29967ef6 XL |
5 | use rustc_hir::{Expr, ExprKind, PathSegment}; |
6 | use rustc_middle::ty; | |
7 | use rustc_span::{symbol::sym, ExpnKind, Span}; | |
8 | ||
9 | declare_lint! { | |
10 | /// The `temporary_cstring_as_ptr` lint detects getting the inner pointer of | |
11 | /// a temporary `CString`. | |
12 | /// | |
13 | /// ### Example | |
14 | /// | |
15 | /// ```rust | |
16 | /// # #![allow(unused)] | |
17 | /// # use std::ffi::CString; | |
18 | /// let c_str = CString::new("foo").unwrap().as_ptr(); | |
19 | /// ``` | |
20 | /// | |
21 | /// {{produces}} | |
22 | /// | |
23 | /// ### Explanation | |
24 | /// | |
25 | /// The inner pointer of a `CString` lives only as long as the `CString` it | |
26 | /// points to. Getting the inner pointer of a *temporary* `CString` allows the `CString` | |
27 | /// to be dropped at the end of the statement, as it is not being referenced as far as the typesystem | |
28 | /// is concerned. This means outside of the statement the pointer will point to freed memory, which | |
29 | /// causes undefined behavior if the pointer is later dereferenced. | |
30 | pub TEMPORARY_CSTRING_AS_PTR, | |
31 | Warn, | |
32 | "detects getting the inner pointer of a temporary `CString`" | |
33 | } | |
34 | ||
35 | declare_lint_pass!(TemporaryCStringAsPtr => [TEMPORARY_CSTRING_AS_PTR]); | |
36 | ||
37 | fn in_macro(span: Span) -> bool { | |
38 | if span.from_expansion() { | |
39 | !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..)) | |
40 | } else { | |
41 | false | |
42 | } | |
43 | } | |
44 | ||
45 | fn first_method_call<'tcx>( | |
46 | expr: &'tcx Expr<'tcx>, | |
f2b60f7d FG |
47 | ) -> Option<(&'tcx PathSegment<'tcx>, &'tcx Expr<'tcx>)> { |
48 | if let ExprKind::MethodCall(path, receiver, args, ..) = &expr.kind { | |
49 | if args.iter().any(|e| e.span.from_expansion()) || receiver.span.from_expansion() { | |
50 | None | |
51 | } else { | |
52 | Some((path, *receiver)) | |
53 | } | |
29967ef6 XL |
54 | } else { |
55 | None | |
56 | } | |
57 | } | |
58 | ||
59 | impl<'tcx> LateLintPass<'tcx> for TemporaryCStringAsPtr { | |
60 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | |
61 | if in_macro(expr.span) { | |
62 | return; | |
63 | } | |
64 | ||
65 | match first_method_call(expr) { | |
f2b60f7d | 66 | Some((path, unwrap_arg)) if path.ident.name == sym::as_ptr => { |
29967ef6 XL |
67 | let as_ptr_span = path.ident.span; |
68 | match first_method_call(unwrap_arg) { | |
f2b60f7d | 69 | Some((path, receiver)) |
29967ef6 XL |
70 | if path.ident.name == sym::unwrap || path.ident.name == sym::expect => |
71 | { | |
f2b60f7d | 72 | lint_cstring_as_ptr(cx, as_ptr_span, receiver, unwrap_arg); |
29967ef6 XL |
73 | } |
74 | _ => return, | |
75 | } | |
76 | } | |
77 | _ => return, | |
78 | } | |
79 | } | |
80 | } | |
81 | ||
82 | fn lint_cstring_as_ptr( | |
83 | cx: &LateContext<'_>, | |
84 | as_ptr_span: Span, | |
85 | source: &rustc_hir::Expr<'_>, | |
86 | unwrap: &rustc_hir::Expr<'_>, | |
87 | ) { | |
88 | let source_type = cx.typeck_results().expr_ty(source); | |
89 | if let ty::Adt(def, substs) = source_type.kind() { | |
5e7ed085 | 90 | if cx.tcx.is_diagnostic_item(sym::Result, def.did()) { |
29967ef6 | 91 | if let ty::Adt(adt, _) = substs.type_at(0).kind() { |
5e7ed085 | 92 | if cx.tcx.is_diagnostic_item(sym::cstring_type, adt.did()) { |
29967ef6 | 93 | cx.struct_span_lint(TEMPORARY_CSTRING_AS_PTR, as_ptr_span, |diag| { |
064997fb FG |
94 | diag.build(fluent::lint::cstring_ptr) |
95 | .span_label(as_ptr_span, fluent::lint::as_ptr_label) | |
96 | .span_label(unwrap.span, fluent::lint::unwrap_label) | |
97 | .note(fluent::lint::note) | |
98 | .help(fluent::lint::help) | |
99 | .emit(); | |
29967ef6 XL |
100 | }); |
101 | } | |
102 | } | |
103 | } | |
104 | } | |
105 | } |