Skip to content

Commit ed09092

Browse files
committed
Auto merge of #3848 - felix91gr:null_transmute, r=<try>
Transmuting known null ptr to ref Working on implementing #628
2 parents 0849f73 + 069957a commit ed09092

File tree

8 files changed

+195
-14
lines changed

8 files changed

+195
-14
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,7 @@ All notable changes to this project will be documented in this file.
10221022
[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
10231023
[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
10241024
[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
1025+
[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
10251026
[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
10261027
[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
10271028
[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
99

10-
[There are 297 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
10+
[There are 298 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1111

1212
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1313

clippy_lints/src/consts.rs

+24-13
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ pub enum Constant {
4141
Repeat(Box<Constant>, u64),
4242
/// A tuple of constants.
4343
Tuple(Vec<Constant>),
44+
/// A raw pointer.
45+
RawPtr(u128),
4446
/// A literal with syntax error.
4547
Err(Symbol),
4648
}
@@ -109,6 +111,9 @@ impl Hash for Constant {
109111
c.hash(state);
110112
l.hash(state);
111113
},
114+
Constant::RawPtr(u) => {
115+
u.hash(state);
116+
},
112117
Constant::Err(ref s) => {
113118
s.hash(state);
114119
},
@@ -192,7 +197,7 @@ pub fn constant_simple<'c, 'cc>(
192197
constant(lcx, tables, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
193198
}
194199

195-
/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckTables`
200+
/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckTables`.
196201
pub fn constant_context<'c, 'cc>(
197202
lcx: &LateContext<'c, 'cc>,
198203
tables: &'c ty::TypeckTables<'cc>,
@@ -215,7 +220,7 @@ pub struct ConstEvalLateContext<'a, 'tcx: 'a> {
215220
}
216221

217222
impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
218-
/// simple constant folding: Insert an expression, get a constant or none.
223+
/// Simple constant folding: Insert an expression, get a constant or none.
219224
pub fn expr(&mut self, e: &Expr) -> Option<Constant> {
220225
match e.node {
221226
ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id),
@@ -238,7 +243,7 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
238243
}),
239244
ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right),
240245
ExprKind::Call(ref callee, ref args) => {
241-
// We only handle a few const functions for now
246+
// We only handle a few const functions for now.
242247
if_chain! {
243248
if args.is_empty();
244249
if let ExprKind::Path(qpath) = &callee.node;
@@ -262,7 +267,7 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
262267
}
263268
}
264269
},
265-
// TODO: add other expressions
270+
// TODO: add other expressions.
266271
_ => None,
267272
}
268273
}
@@ -304,13 +309,13 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
304309
}
305310
}
306311

307-
/// create `Some(Vec![..])` of all constants, unless there is any
308-
/// non-constant part
312+
/// Create `Some(Vec![..])` of all constants, unless there is any
313+
/// non-constant part.
309314
fn multi(&mut self, vec: &[Expr]) -> Option<Vec<Constant>> {
310315
vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
311316
}
312317

313-
/// lookup a possibly constant expression from a ExprKind::Path
318+
/// Lookup a possibly constant expression from a ExprKind::Path.
314319
fn fetch_path(&mut self, qpath: &QPath, id: HirId) -> Option<Constant> {
315320
use rustc::mir::interpret::GlobalId;
316321

@@ -334,14 +339,14 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
334339
if ret.is_some() {
335340
self.needed_resolution = true;
336341
}
337-
return ret;
342+
ret
338343
},
339-
_ => {},
344+
// FIXME: cover all useable cases.
345+
_ => None,
340346
}
341-
None
342347
}
343348

344-
/// A block can only yield a constant if it only has one constant expression
349+
/// A block can only yield a constant if it only has one constant expression.
345350
fn block(&mut self, block: &Block) -> Option<Constant> {
346351
if block.stmts.is_empty() {
347352
block.expr.as_ref().and_then(|b| self.expr(b))
@@ -467,7 +472,13 @@ pub fn miri_to_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, result: &ty::Const<'
467472
ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
468473
b.try_into().expect("invalid f64 bit representation"),
469474
))),
470-
// FIXME: implement other conversion
475+
ty::RawPtr(type_and_mut) => {
476+
if let ty::Uint(_) = type_and_mut.ty.sty {
477+
return Some(Constant::RawPtr(b));
478+
}
479+
None
480+
},
481+
// FIXME: implement other conversions.
471482
_ => None,
472483
},
473484
ConstValue::Slice(Scalar::Ptr(ptr), n) => match result.ty.sty {
@@ -484,7 +495,7 @@ pub fn miri_to_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, result: &ty::Const<'
484495
},
485496
_ => None,
486497
},
487-
// FIXME: implement other conversions
498+
// FIXME: implement other conversions.
488499
_ => None,
489500
}
490501
}

clippy_lints/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ pub mod suspicious_trait_impl;
255255
pub mod swap;
256256
pub mod temporary_assignment;
257257
pub mod transmute;
258+
pub mod transmuting_null;
258259
pub mod trivially_copy_pass_by_ref;
259260
pub mod types;
260261
pub mod unicode;
@@ -570,6 +571,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
570571
reg.register_late_lint_pass(box types::RefToMut);
571572
reg.register_late_lint_pass(box assertions_on_constants::AssertionsOnConstants);
572573
reg.register_late_lint_pass(box missing_const_for_fn::MissingConstForFn);
574+
reg.register_late_lint_pass(box transmuting_null::Pass);
573575

574576
reg.register_lint_group("clippy::restriction", Some("clippy_restriction"), vec![
575577
arithmetic::FLOAT_ARITHMETIC,
@@ -841,6 +843,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
841843
transmute::TRANSMUTE_PTR_TO_REF,
842844
transmute::USELESS_TRANSMUTE,
843845
transmute::WRONG_TRANSMUTE,
846+
transmuting_null::TRANSMUTING_NULL,
844847
trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF,
845848
types::ABSURD_EXTREME_COMPARISONS,
846849
types::BORROWED_BOX,
@@ -1078,6 +1081,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
10781081
suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
10791082
swap::ALMOST_SWAPPED,
10801083
transmute::WRONG_TRANSMUTE,
1084+
transmuting_null::TRANSMUTING_NULL,
10811085
types::ABSURD_EXTREME_COMPARISONS,
10821086
types::CAST_PTR_ALIGNMENT,
10831087
types::CAST_REF_TO_MUT,

clippy_lints/src/transmuting_null.rs

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
use crate::consts::{constant_context, Constant};
2+
use crate::utils::{match_qpath, span_lint};
3+
use if_chain::if_chain;
4+
use rustc::hir::{Expr, ExprKind};
5+
use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintContext, LintPass};
6+
use rustc::{declare_tool_lint, lint_array};
7+
use syntax::ast::LitKind;
8+
9+
declare_clippy_lint! {
10+
/// **What it does:** Checks for transmute calls which would receive a null pointer.
11+
///
12+
/// **Why is this bad?** Transmuting a null pointer is undefined behavior.
13+
///
14+
/// **Known problems:** Not all cases can be detected at the moment of this writing.
15+
/// For example, variables which hold a null pointer and are then fed to a `transmute`
16+
/// call, aren't detectable yet.
17+
///
18+
/// **Example:**
19+
/// ```rust
20+
/// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
21+
/// ```
22+
pub TRANSMUTING_NULL,
23+
correctness,
24+
"transmutes from a null pointer to a reference, which is undefined behavior"
25+
}
26+
27+
#[derive(Copy, Clone)]
28+
pub struct Pass;
29+
30+
impl LintPass for Pass {
31+
fn get_lints(&self) -> LintArray {
32+
lint_array!(TRANSMUTING_NULL,)
33+
}
34+
35+
fn name(&self) -> &'static str {
36+
"TransmutingNull"
37+
}
38+
}
39+
40+
const LINT_MSG: &str = "transmuting a known null pointer into a reference.";
41+
42+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
43+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
44+
if in_external_macro(cx.sess(), expr.span) {
45+
return;
46+
}
47+
48+
if_chain! {
49+
if let ExprKind::Call(ref func, ref args) = expr.node;
50+
if let ExprKind::Path(ref path) = func.node;
51+
if match_qpath(path, &["std", "mem", "transmute"]);
52+
if args.len() == 1;
53+
54+
then {
55+
56+
// Catching transmute over constants that resolve to `null`.
57+
let mut const_eval_context = constant_context(cx, cx.tables);
58+
if_chain! {
59+
if let ExprKind::Path(ref _qpath) = args[0].node;
60+
let x = const_eval_context.expr(&args[0]);
61+
if let Some(constant) = x;
62+
if let Constant::RawPtr(ptr_value) = constant;
63+
if ptr_value == 0;
64+
then {
65+
span_lint(
66+
cx,
67+
TRANSMUTING_NULL,
68+
expr.span,
69+
LINT_MSG)
70+
}
71+
}
72+
73+
// Catching:
74+
// `std::mem::transmute(0 as *const i32)`
75+
if_chain! {
76+
if let ExprKind::Cast(ref inner_expr, ref _cast_ty) = args[0].node;
77+
if let ExprKind::Lit(ref lit) = inner_expr.node;
78+
if let LitKind::Int(0, _) = lit.node;
79+
then {
80+
span_lint(
81+
cx,
82+
TRANSMUTING_NULL,
83+
expr.span,
84+
LINT_MSG)
85+
}
86+
}
87+
88+
// Catching:
89+
// `std::mem::transmute(std::ptr::null::<i32>())`
90+
if_chain! {
91+
if let ExprKind::Call(ref func1, ref args1) = args[0].node;
92+
if let ExprKind::Path(ref path1) = func1.node;
93+
if match_qpath(path1, &["std", "ptr", "null"]);
94+
if args1.len() == 0;
95+
then {
96+
span_lint(
97+
cx,
98+
TRANSMUTING_NULL,
99+
expr.span,
100+
LINT_MSG)
101+
}
102+
}
103+
104+
// FIXME:
105+
// Also catch transmutations of variables which are known nulls.
106+
// To do this, MIR const propagation seems to be the better tool.
107+
// Whenever MIR const prop routines are more developed, this will
108+
// become available. As of this writing (25/03/19) it is not yet.
109+
}
110+
}
111+
}
112+
}

tests/ui/issue_3849.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![allow(dead_code)]
22
#![allow(clippy::zero_ptr)]
33
#![allow(clippy::transmute_ptr_to_ref)]
4+
#![allow(clippy::transmuting_null)]
45

56
pub const ZPTR: *const usize = 0 as *const _;
67

tests/ui/transmuting_null.rs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#![allow(dead_code)]
2+
#![warn(clippy::transmuting_null)]
3+
#![allow(clippy::zero_ptr)]
4+
#![allow(clippy::transmute_ptr_to_ref)]
5+
#![allow(clippy::eq_op)]
6+
7+
// Easy to lint because these only span one line.
8+
fn one_liners() {
9+
unsafe {
10+
let _: &u64 = std::mem::transmute(0 as *const u64);
11+
let _: &u64 = std::mem::transmute(std::ptr::null::<u64>());
12+
}
13+
}
14+
15+
pub const ZPTR: *const usize = 0 as *const _;
16+
pub const NOT_ZPTR: *const usize = 1 as *const _;
17+
18+
fn transmute_const() {
19+
unsafe {
20+
// Should raise a lint.
21+
let _: &u64 = std::mem::transmute(ZPTR);
22+
// Should NOT raise a lint.
23+
let _: &u64 = std::mem::transmute(NOT_ZPTR);
24+
}
25+
}
26+
27+
fn main() {
28+
one_liners();
29+
transmute_const();
30+
}

tests/ui/transmuting_null.stderr

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: transmuting a known null pointer into a reference.
2+
--> $DIR/transmuting_null.rs:10:23
3+
|
4+
LL | let _: &u64 = std::mem::transmute(0 as *const u64);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::transmuting-null` implied by `-D warnings`
8+
9+
error: transmuting a known null pointer into a reference.
10+
--> $DIR/transmuting_null.rs:11:23
11+
|
12+
LL | let _: &u64 = std::mem::transmute(std::ptr::null::<u64>());
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
15+
error: transmuting a known null pointer into a reference.
16+
--> $DIR/transmuting_null.rs:21:23
17+
|
18+
LL | let _: &u64 = std::mem::transmute(ZPTR);
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
20+
21+
error: aborting due to 3 previous errors
22+

0 commit comments

Comments
 (0)