Skip to content

Commit 6afe547

Browse files
committed
Add lint transmute_null_to_fn
1 parent 4bdfb07 commit 6afe547

File tree

6 files changed

+150
-0
lines changed

6 files changed

+150
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4590,6 +4590,7 @@ Released 2018-09-13
45904590
[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
45914591
[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
45924592
[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
4593+
[`transmute_null_to_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_null_to_fn
45934594
[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
45944595
[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
45954596
[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
568568
crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO,
569569
crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO,
570570
crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO,
571+
crate::transmute::TRANSMUTE_NULL_TO_FN_INFO,
571572
crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO,
572573
crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO,
573574
crate::transmute::TRANSMUTE_PTR_TO_REF_INFO,

clippy_lints/src/transmute/mod.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod transmute_float_to_int;
33
mod transmute_int_to_bool;
44
mod transmute_int_to_char;
55
mod transmute_int_to_float;
6+
mod transmute_null_to_fn;
67
mod transmute_num_to_bytes;
78
mod transmute_ptr_to_ptr;
89
mod transmute_ptr_to_ref;
@@ -409,6 +410,34 @@ declare_clippy_lint! {
409410
"transmutes from a null pointer to a reference, which is undefined behavior"
410411
}
411412

413+
declare_clippy_lint! {
414+
/// ### What it does
415+
/// Checks for null function pointer creation through transmute.
416+
///
417+
/// ### Why is this bad?
418+
/// Creating a null function pointer is undefined behavior.
419+
///
420+
/// More info: https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization
421+
///
422+
/// ### Known problems
423+
/// Not all cases can be detected at the moment of this writing.
424+
/// For example, variables which hold a null pointer and are then fed to a `transmute`
425+
/// call, aren't detectable yet.
426+
///
427+
/// ### Example
428+
/// ```rust
429+
/// let null_fn: fn() = unsafe { std::mem::transmute( std::ptr::null() ) };
430+
/// ```
431+
/// Use instead:
432+
/// ```rust
433+
/// let null_fn: Option<fn()> = None;
434+
/// ```
435+
#[clippy::version = "1.67.0"]
436+
pub TRANSMUTE_NULL_TO_FN,
437+
correctness,
438+
"transmute results in a null function pointer, which is undefined behavior"
439+
}
440+
412441
pub struct Transmute {
413442
msrv: Msrv,
414443
}
@@ -428,6 +457,7 @@ impl_lint_pass!(Transmute => [
428457
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
429458
TRANSMUTE_UNDEFINED_REPR,
430459
TRANSMUTING_NULL,
460+
TRANSMUTE_NULL_TO_FN,
431461
]);
432462
impl Transmute {
433463
#[must_use]
@@ -461,6 +491,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
461491
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
462492
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
463493
| transmuting_null::check(cx, e, arg, to_ty)
494+
| transmute_null_to_fn::check(cx, e, arg, to_ty)
464495
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv)
465496
| transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
466497
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use clippy_utils::consts::{constant_context, Constant};
2+
use clippy_utils::diagnostics::span_lint_and_then;
3+
use clippy_utils::{is_integer_literal, is_path_diagnostic_item};
4+
use rustc_hir::{Expr, ExprKind};
5+
use rustc_lint::LateContext;
6+
use rustc_middle::ty::Ty;
7+
use rustc_span::symbol::sym;
8+
9+
use super::TRANSMUTE_NULL_TO_FN;
10+
11+
const LINT_MSG: &str = "transmuting a known null pointer into a function pointer";
12+
const NOTE_MSG: &str = "this transmute results in a null function pointer";
13+
const HELP_MSG: &str =
14+
"try wrapping your function pointer type in `Option<T>` instead, and using `None` as a null pointer value";
15+
16+
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool {
17+
if !to_ty.is_fn() {
18+
return false;
19+
}
20+
21+
// Catching transmute over constants that resolve to `null`.
22+
let mut const_eval_context = constant_context(cx, cx.typeck_results());
23+
if let ExprKind::Path(ref _qpath) = arg.kind &&
24+
let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg) &&
25+
x == 0
26+
{
27+
span_lint_and_then(cx, TRANSMUTE_NULL_TO_FN, expr.span, LINT_MSG, |diag| {
28+
diag.span_label(expr.span, NOTE_MSG);
29+
diag.help(HELP_MSG);
30+
});
31+
return true;
32+
}
33+
34+
// Catching:
35+
// `std::mem::transmute(0 as *const i32)`
36+
if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind && is_integer_literal(inner_expr, 0) {
37+
span_lint_and_then(cx, TRANSMUTE_NULL_TO_FN, expr.span, LINT_MSG, |diag| {
38+
diag.span_label(expr.span, NOTE_MSG);
39+
diag.help(HELP_MSG);
40+
});
41+
return true;
42+
}
43+
44+
// Catching:
45+
// `std::mem::transmute(std::ptr::null::<i32>())`
46+
if let ExprKind::Call(func1, []) = arg.kind &&
47+
is_path_diagnostic_item(cx, func1, sym::ptr_null)
48+
{
49+
span_lint_and_then(cx, TRANSMUTE_NULL_TO_FN, expr.span, LINT_MSG, |diag| {
50+
diag.span_label(expr.span, NOTE_MSG);
51+
diag.help(HELP_MSG);
52+
});
53+
return true;
54+
}
55+
56+
// FIXME:
57+
// Also catch transmutations of variables which are known nulls.
58+
// To do this, MIR const propagation seems to be the better tool.
59+
// Whenever MIR const prop routines are more developed, this will
60+
// become available. As of this writing (25/03/19) it is not yet.
61+
false
62+
}

tests/ui/transmute_null_to_fn.rs

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

tests/ui/transmute_null_to_fn.stderr

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error: transmuting a known null pointer into a function pointer
2+
--> $DIR/transmute_null_to_fn.rs:8:23
3+
|
4+
LL | let _: fn() = std::mem::transmute(0 as *const ());
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior
6+
|
7+
= help: try wrapping your function pointer type in `Option<T>` instead, and using `None` as a null pointer value
8+
= note: `-D clippy::transmute-null-to-fn` implied by `-D warnings`
9+
10+
error: transmuting a known null pointer into a function pointer
11+
--> $DIR/transmute_null_to_fn.rs:9:23
12+
|
13+
LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>());
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior
15+
|
16+
= help: try wrapping your function pointer type in `Option<T>` instead, and using `None` as a null pointer value
17+
18+
error: transmuting a known null pointer into a function pointer
19+
--> $DIR/transmute_null_to_fn.rs:19:23
20+
|
21+
LL | let _: fn() = std::mem::transmute(ZPTR);
22+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior
23+
|
24+
= help: try wrapping your function pointer type in `Option<T>` instead, and using `None` as a null pointer value
25+
26+
error: aborting due to 3 previous errors
27+

0 commit comments

Comments
 (0)