Move TransmutingNull into Transmute lint pass

This commit is contained in:
Jason Newcomb 2022-06-05 21:36:40 -04:00
parent e834855950
commit e213b6ee35
7 changed files with 89 additions and 94 deletions

View File

@ -315,10 +315,10 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
LintId::of(transmute::TRANSMUTE_PTR_TO_REF), LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
LintId::of(transmute::TRANSMUTING_NULL),
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(transmute::USELESS_TRANSMUTE),
LintId::of(transmute::WRONG_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE),
LintId::of(transmuting_null::TRANSMUTING_NULL),
LintId::of(types::BORROWED_BOX), LintId::of(types::BORROWED_BOX),
LintId::of(types::BOX_COLLECTION), LintId::of(types::BOX_COLLECTION),
LintId::of(types::REDUNDANT_ALLOCATION), LintId::of(types::REDUNDANT_ALLOCATION),

View File

@ -62,9 +62,9 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
LintId::of(serde_api::SERDE_API_MISUSE), LintId::of(serde_api::SERDE_API_MISUSE),
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
LintId::of(swap::ALMOST_SWAPPED), LintId::of(swap::ALMOST_SWAPPED),
LintId::of(transmute::TRANSMUTING_NULL),
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
LintId::of(transmute::WRONG_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE),
LintId::of(transmuting_null::TRANSMUTING_NULL),
LintId::of(unicode::INVISIBLE_CHARACTERS), LintId::of(unicode::INVISIBLE_CHARACTERS),
LintId::of(uninit_vec::UNINIT_VEC), LintId::of(uninit_vec::UNINIT_VEC),
LintId::of(unit_hash::UNIT_HASH), LintId::of(unit_hash::UNIT_HASH),

View File

@ -540,10 +540,10 @@ store.register_lints(&[
transmute::TRANSMUTE_PTR_TO_PTR, transmute::TRANSMUTE_PTR_TO_PTR,
transmute::TRANSMUTE_PTR_TO_REF, transmute::TRANSMUTE_PTR_TO_REF,
transmute::TRANSMUTE_UNDEFINED_REPR, transmute::TRANSMUTE_UNDEFINED_REPR,
transmute::TRANSMUTING_NULL,
transmute::UNSOUND_COLLECTION_TRANSMUTE, transmute::UNSOUND_COLLECTION_TRANSMUTE,
transmute::USELESS_TRANSMUTE, transmute::USELESS_TRANSMUTE,
transmute::WRONG_TRANSMUTE, transmute::WRONG_TRANSMUTE,
transmuting_null::TRANSMUTING_NULL,
types::BORROWED_BOX, types::BORROWED_BOX,
types::BOX_COLLECTION, types::BOX_COLLECTION,
types::LINKEDLIST, types::LINKEDLIST,

View File

@ -367,7 +367,6 @@ mod to_digit_is_some;
mod trailing_empty_array; mod trailing_empty_array;
mod trait_bounds; mod trait_bounds;
mod transmute; mod transmute;
mod transmuting_null;
mod types; mod types;
mod undocumented_unsafe_blocks; mod undocumented_unsafe_blocks;
mod unicode; mod unicode;
@ -723,7 +722,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api))); store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants)); store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates)); store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates));
store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
store.register_late_pass(|| Box::new(inherent_to_string::InherentToString)); store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
let max_trait_bounds = conf.max_trait_bounds; let max_trait_bounds = conf.max_trait_bounds;
store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds))); store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));

View File

@ -9,6 +9,7 @@ mod transmute_ptr_to_ref;
mod transmute_ref_to_ref; mod transmute_ref_to_ref;
mod transmute_undefined_repr; mod transmute_undefined_repr;
mod transmutes_expressible_as_ptr_casts; mod transmutes_expressible_as_ptr_casts;
mod transmuting_null;
mod unsound_collection_transmute; mod unsound_collection_transmute;
mod useless_transmute; mod useless_transmute;
mod utils; mod utils;
@ -386,6 +387,28 @@ declare_clippy_lint! {
"transmute to or from a type with an undefined representation" "transmute to or from a type with an undefined representation"
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for transmute calls which would receive a null pointer.
///
/// ### Why is this bad?
/// Transmuting a null pointer is undefined behavior.
///
/// ### 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_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
/// ```
#[clippy::version = "1.35.0"]
pub TRANSMUTING_NULL,
correctness,
"transmutes from a null pointer to a reference, which is undefined behavior"
}
pub struct Transmute { pub struct Transmute {
msrv: Option<RustcVersion>, msrv: Option<RustcVersion>,
} }
@ -404,6 +427,7 @@ impl_lint_pass!(Transmute => [
UNSOUND_COLLECTION_TRANSMUTE, UNSOUND_COLLECTION_TRANSMUTE,
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
TRANSMUTE_UNDEFINED_REPR, TRANSMUTE_UNDEFINED_REPR,
TRANSMUTING_NULL,
]); ]);
impl Transmute { impl Transmute {
#[must_use] #[must_use]
@ -436,6 +460,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
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)
| 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)

View File

@ -0,0 +1,61 @@
use clippy_utils::consts::{constant_context, Constant};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_expr_diagnostic_item;
use if_chain::if_chain;
use rustc_ast::LitKind;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::Ty;
use rustc_span::symbol::sym;
use super::TRANSMUTING_NULL;
const LINT_MSG: &str = "transmuting a known null pointer into a reference";
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool {
if !to_ty.is_ref() {
return false;
}
// Catching transmute over constants that resolve to `null`.
let mut const_eval_context = constant_context(cx, cx.typeck_results());
if_chain! {
if let ExprKind::Path(ref _qpath) = arg.kind;
if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg);
if x == 0;
then {
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
return true;
}
}
// Catching:
// `std::mem::transmute(0 as *const i32)`
if_chain! {
if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
if let ExprKind::Lit(ref lit) = inner_expr.kind;
if let LitKind::Int(0, _) = lit.node;
then {
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
return true;
}
}
// Catching:
// `std::mem::transmute(std::ptr::null::<i32>())`
if_chain! {
if let ExprKind::Call(func1, []) = arg.kind;
if is_expr_diagnostic_item(cx, func1, sym::ptr_null);
then {
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_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
}

View File

@ -1,89 +0,0 @@
use clippy_utils::consts::{constant_context, Constant};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_expr_diagnostic_item;
use if_chain::if_chain;
use rustc_ast::LitKind;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;
declare_clippy_lint! {
/// ### What it does
/// Checks for transmute calls which would receive a null pointer.
///
/// ### Why is this bad?
/// Transmuting a null pointer is undefined behavior.
///
/// ### 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_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
/// ```
#[clippy::version = "1.35.0"]
pub TRANSMUTING_NULL,
correctness,
"transmutes from a null pointer to a reference, which is undefined behavior"
}
declare_lint_pass!(TransmutingNull => [TRANSMUTING_NULL]);
const LINT_MSG: &str = "transmuting a known null pointer into a reference";
impl<'tcx> LateLintPass<'tcx> for TransmutingNull {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if in_external_macro(cx.sess(), expr.span) {
return;
}
if_chain! {
if let ExprKind::Call(func, [arg]) = expr.kind;
if is_expr_diagnostic_item(cx, func, sym::transmute);
then {
// Catching transmute over constants that resolve to `null`.
let mut const_eval_context = constant_context(cx, cx.typeck_results());
if_chain! {
if let ExprKind::Path(ref _qpath) = arg.kind;
if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg);
if x == 0;
then {
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
}
}
// Catching:
// `std::mem::transmute(0 as *const i32)`
if_chain! {
if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
if let ExprKind::Lit(ref lit) = inner_expr.kind;
if let LitKind::Int(0, _) = lit.node;
then {
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
}
}
// Catching:
// `std::mem::transmute(std::ptr::null::<i32>())`
if_chain! {
if let ExprKind::Call(func1, []) = arg.kind;
if is_expr_diagnostic_item(cx, func1, sym::ptr_null);
then {
span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG)
}
}
// 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.
}
}
}
}