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