Add lint transmute_null_to_fn
This commit is contained in:
parent
4bdfb0741d
commit
6afe5471cf
@ -4590,6 +4590,7 @@ Released 2018-09-13
|
|||||||
[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
|
[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
|
||||||
[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
|
[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
|
||||||
[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
|
[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
|
||||||
|
[`transmute_null_to_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_null_to_fn
|
||||||
[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
|
[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
|
||||||
[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
|
[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
|
||||||
[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
|
[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
|
||||||
|
@ -568,6 +568,7 @@
|
|||||||
crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO,
|
crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO,
|
||||||
crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO,
|
crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO,
|
||||||
crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO,
|
crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO,
|
||||||
|
crate::transmute::TRANSMUTE_NULL_TO_FN_INFO,
|
||||||
crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO,
|
crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO,
|
||||||
crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO,
|
crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO,
|
||||||
crate::transmute::TRANSMUTE_PTR_TO_REF_INFO,
|
crate::transmute::TRANSMUTE_PTR_TO_REF_INFO,
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
mod transmute_int_to_bool;
|
mod transmute_int_to_bool;
|
||||||
mod transmute_int_to_char;
|
mod transmute_int_to_char;
|
||||||
mod transmute_int_to_float;
|
mod transmute_int_to_float;
|
||||||
|
mod transmute_null_to_fn;
|
||||||
mod transmute_num_to_bytes;
|
mod transmute_num_to_bytes;
|
||||||
mod transmute_ptr_to_ptr;
|
mod transmute_ptr_to_ptr;
|
||||||
mod transmute_ptr_to_ref;
|
mod transmute_ptr_to_ref;
|
||||||
@ -409,6 +410,34 @@
|
|||||||
"transmutes from a null pointer to a reference, which is undefined behavior"
|
"transmutes from a null pointer to a reference, which is undefined behavior"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Checks for null function pointer creation through transmute.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// Creating a null function pointer is undefined behavior.
|
||||||
|
///
|
||||||
|
/// More info: https://doc.rust-lang.org/nomicon/ffi.html#the-nullable-pointer-optimization
|
||||||
|
///
|
||||||
|
/// ### Known problems
|
||||||
|
/// Not all cases can be detected at the moment of this writing.
|
||||||
|
/// For example, variables which hold a null pointer and are then fed to a `transmute`
|
||||||
|
/// call, aren't detectable yet.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust
|
||||||
|
/// let null_fn: fn() = unsafe { std::mem::transmute( std::ptr::null() ) };
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// let null_fn: Option<fn()> = None;
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.67.0"]
|
||||||
|
pub TRANSMUTE_NULL_TO_FN,
|
||||||
|
correctness,
|
||||||
|
"transmute results in a null function pointer, which is undefined behavior"
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Transmute {
|
pub struct Transmute {
|
||||||
msrv: Msrv,
|
msrv: Msrv,
|
||||||
}
|
}
|
||||||
@ -428,6 +457,7 @@ pub struct Transmute {
|
|||||||
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
|
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
|
||||||
TRANSMUTE_UNDEFINED_REPR,
|
TRANSMUTE_UNDEFINED_REPR,
|
||||||
TRANSMUTING_NULL,
|
TRANSMUTING_NULL,
|
||||||
|
TRANSMUTE_NULL_TO_FN,
|
||||||
]);
|
]);
|
||||||
impl Transmute {
|
impl Transmute {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -461,6 +491,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
|||||||
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
|
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
|
||||||
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
|
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
|
||||||
| transmuting_null::check(cx, e, arg, to_ty)
|
| transmuting_null::check(cx, e, arg, to_ty)
|
||||||
|
| transmute_null_to_fn::check(cx, e, arg, to_ty)
|
||||||
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv)
|
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv)
|
||||||
| transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
|
| transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||||
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
|
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||||
|
62
clippy_lints/src/transmute/transmute_null_to_fn.rs
Normal file
62
clippy_lints/src/transmute/transmute_null_to_fn.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
use clippy_utils::consts::{constant_context, Constant};
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
|
use clippy_utils::{is_integer_literal, is_path_diagnostic_item};
|
||||||
|
use rustc_hir::{Expr, ExprKind};
|
||||||
|
use rustc_lint::LateContext;
|
||||||
|
use rustc_middle::ty::Ty;
|
||||||
|
use rustc_span::symbol::sym;
|
||||||
|
|
||||||
|
use super::TRANSMUTE_NULL_TO_FN;
|
||||||
|
|
||||||
|
const LINT_MSG: &str = "transmuting a known null pointer into a function pointer";
|
||||||
|
const NOTE_MSG: &str = "this transmute results in a null function pointer";
|
||||||
|
const HELP_MSG: &str =
|
||||||
|
"try wrapping your function pointer type in `Option<T>` instead, and using `None` as a null pointer value";
|
||||||
|
|
||||||
|
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool {
|
||||||
|
if !to_ty.is_fn() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Catching transmute over constants that resolve to `null`.
|
||||||
|
let mut const_eval_context = constant_context(cx, cx.typeck_results());
|
||||||
|
if let ExprKind::Path(ref _qpath) = arg.kind &&
|
||||||
|
let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg) &&
|
||||||
|
x == 0
|
||||||
|
{
|
||||||
|
span_lint_and_then(cx, TRANSMUTE_NULL_TO_FN, expr.span, LINT_MSG, |diag| {
|
||||||
|
diag.span_label(expr.span, NOTE_MSG);
|
||||||
|
diag.help(HELP_MSG);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Catching:
|
||||||
|
// `std::mem::transmute(0 as *const i32)`
|
||||||
|
if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind && is_integer_literal(inner_expr, 0) {
|
||||||
|
span_lint_and_then(cx, TRANSMUTE_NULL_TO_FN, expr.span, LINT_MSG, |diag| {
|
||||||
|
diag.span_label(expr.span, NOTE_MSG);
|
||||||
|
diag.help(HELP_MSG);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Catching:
|
||||||
|
// `std::mem::transmute(std::ptr::null::<i32>())`
|
||||||
|
if let ExprKind::Call(func1, []) = arg.kind &&
|
||||||
|
is_path_diagnostic_item(cx, func1, sym::ptr_null)
|
||||||
|
{
|
||||||
|
span_lint_and_then(cx, TRANSMUTE_NULL_TO_FN, expr.span, LINT_MSG, |diag| {
|
||||||
|
diag.span_label(expr.span, NOTE_MSG);
|
||||||
|
diag.help(HELP_MSG);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME:
|
||||||
|
// Also catch transmutations of variables which are known nulls.
|
||||||
|
// To do this, MIR const propagation seems to be the better tool.
|
||||||
|
// Whenever MIR const prop routines are more developed, this will
|
||||||
|
// become available. As of this writing (25/03/19) it is not yet.
|
||||||
|
false
|
||||||
|
}
|
28
tests/ui/transmute_null_to_fn.rs
Normal file
28
tests/ui/transmute_null_to_fn.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
#![warn(clippy::transmute_null_to_fn)]
|
||||||
|
#![allow(clippy::zero_ptr)]
|
||||||
|
|
||||||
|
// Easy to lint because these only span one line.
|
||||||
|
fn one_liners() {
|
||||||
|
unsafe {
|
||||||
|
let _: fn() = std::mem::transmute(0 as *const ());
|
||||||
|
let _: fn() = std::mem::transmute(std::ptr::null::<()>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const ZPTR: *const usize = 0 as *const _;
|
||||||
|
pub const NOT_ZPTR: *const usize = 1 as *const _;
|
||||||
|
|
||||||
|
fn transmute_const() {
|
||||||
|
unsafe {
|
||||||
|
// Should raise a lint.
|
||||||
|
let _: fn() = std::mem::transmute(ZPTR);
|
||||||
|
// Should NOT raise a lint.
|
||||||
|
let _: fn() = std::mem::transmute(NOT_ZPTR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
one_liners();
|
||||||
|
transmute_const();
|
||||||
|
}
|
27
tests/ui/transmute_null_to_fn.stderr
Normal file
27
tests/ui/transmute_null_to_fn.stderr
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
error: transmuting a known null pointer into a function pointer
|
||||||
|
--> $DIR/transmute_null_to_fn.rs:8:23
|
||||||
|
|
|
||||||
|
LL | let _: fn() = std::mem::transmute(0 as *const ());
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior
|
||||||
|
|
|
||||||
|
= help: try wrapping your function pointer type in `Option<T>` instead, and using `None` as a null pointer value
|
||||||
|
= note: `-D clippy::transmute-null-to-fn` implied by `-D warnings`
|
||||||
|
|
||||||
|
error: transmuting a known null pointer into a function pointer
|
||||||
|
--> $DIR/transmute_null_to_fn.rs:9:23
|
||||||
|
|
|
||||||
|
LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>());
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior
|
||||||
|
|
|
||||||
|
= help: try wrapping your function pointer type in `Option<T>` instead, and using `None` as a null pointer value
|
||||||
|
|
||||||
|
error: transmuting a known null pointer into a function pointer
|
||||||
|
--> $DIR/transmute_null_to_fn.rs:19:23
|
||||||
|
|
|
||||||
|
LL | let _: fn() = std::mem::transmute(ZPTR);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior
|
||||||
|
|
|
||||||
|
= help: try wrapping your function pointer type in `Option<T>` instead, and using `None` as a null pointer value
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
Loading…
Reference in New Issue
Block a user