-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
159 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
use clippy_utils::diagnostics::span_lint_and_then; | ||
use rustc_ast::InlineAsmOptions; | ||
use rustc_hir::{Expr, ExprKind, InlineAsm, InlineAsmOperand}; | ||
use rustc_lint::{LateContext, LateLintPass}; | ||
use rustc_session::declare_lint_pass; | ||
use rustc_span::Span; | ||
|
||
declare_clippy_lint! { | ||
/// ### What it does | ||
/// Checks if any pointer is being passed to an asm! block with `nomem` option. | ||
/// | ||
/// ### Why is this bad? | ||
/// `nomem` forbids any reads or writes to memory and passing a pointer suggests | ||
/// that either of those will happen. | ||
/// | ||
/// ### Example | ||
/// ```no_run | ||
/// fn f(p: *mut u32) { | ||
/// unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nomem, nostack)); } | ||
/// } | ||
/// ``` | ||
/// Use instead: | ||
/// ```no_run | ||
/// fn f(p: *mut u32) { | ||
/// unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nostack)); } | ||
/// } | ||
/// ``` | ||
#[clippy::version = "1.81.0"] | ||
pub POINTER_IN_NOMEM_ASM_BLOCK, | ||
suspicious, | ||
"pointer in nomem asm block" | ||
} | ||
|
||
declare_lint_pass!(PointerInNomemAsmBlock => [POINTER_IN_NOMEM_ASM_BLOCK]); | ||
|
||
impl<'tcx> LateLintPass<'tcx> for PointerInNomemAsmBlock { | ||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { | ||
if let ExprKind::InlineAsm(asm) = &expr.kind { | ||
check_asm(cx, asm); | ||
} | ||
} | ||
} | ||
|
||
fn check_asm(cx: &LateContext<'_>, asm: &InlineAsm<'_>) { | ||
if !asm.options.contains(InlineAsmOptions::NOMEM) { | ||
return; | ||
} | ||
|
||
let spans = asm | ||
.operands | ||
.iter() | ||
.filter(|(op, _span)| has_in_operand_pointer(cx, op)) | ||
.map(|(_op, span)| *span) | ||
.collect::<Vec<Span>>(); | ||
|
||
if spans.is_empty() { | ||
return; | ||
} | ||
|
||
span_lint_and_then( | ||
cx, | ||
POINTER_IN_NOMEM_ASM_BLOCK, | ||
spans, | ||
"passing pointer to nomem asm block", | ||
additional_notes, | ||
); | ||
} | ||
|
||
fn has_in_operand_pointer(cx: &LateContext<'_>, asm_op: &InlineAsmOperand<'_>) -> bool { | ||
let asm_in_expr = match asm_op { | ||
InlineAsmOperand::SymStatic { .. } | ||
| InlineAsmOperand::Out { .. } | ||
| InlineAsmOperand::Const { .. } | ||
| InlineAsmOperand::SymFn { .. } | ||
| InlineAsmOperand::Label { .. } => return false, | ||
InlineAsmOperand::SplitInOut { in_expr, .. } => in_expr, | ||
InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => expr, | ||
}; | ||
|
||
// This checks for raw ptrs, refs and function pointers - the last one | ||
// also technically counts as reading memory. | ||
cx.typeck_results().expr_ty(asm_in_expr).is_any_ptr() | ||
} | ||
|
||
fn additional_notes(diag: &mut rustc_errors::Diag<'_, ()>) { | ||
diag.note("`nomem` means that no memory write or read happens inside them asm! block"); | ||
diag.note("if this is intentional and no pointers are read or written to, consider allowing the lint"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
//@ needs-asm-support | ||
#![warn(clippy::pointer_in_nomem_asm_block)] | ||
#![crate_type = "lib"] | ||
#![no_std] | ||
|
||
use core::arch::asm; | ||
|
||
unsafe fn nomem_bad(p: &i32) { | ||
asm!( | ||
"asdf {p1}, {p2}, {p3}", | ||
p1 = in(reg) p, | ||
//~^ ERROR: passing pointer to nomem asm block | ||
p2 = in(reg) p as *const _ as usize, | ||
p3 = in(reg) p, | ||
options(nomem, nostack, preserves_flags) | ||
); | ||
} | ||
|
||
unsafe fn nomem_good(p: &i32) { | ||
asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags)); | ||
let p = p as *const i32 as usize; | ||
asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); | ||
} | ||
|
||
unsafe fn nomem_bad2(p: &mut i32) { | ||
asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); | ||
//~^ ERROR: passing pointer to nomem asm block | ||
} | ||
|
||
unsafe fn nomem_fn(p: extern "C" fn()) { | ||
asm!("call {p}", p = in(reg) p, options(nomem)); | ||
//~^ ERROR: passing pointer to nomem asm block | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
error: passing pointer to nomem asm block | ||
--> tests/ui/pointer_in_nomem_asm_block.rs:11:9 | ||
| | ||
LL | p1 = in(reg) p, | ||
| ^^^^^^^^^^^^^^ | ||
... | ||
LL | p3 = in(reg) p, | ||
| ^^^^^^^^^^^^^^ | ||
| | ||
= note: `nomem` means that no memory write or read happens inside them asm! block | ||
= note: if this is intentional and no pointers are read or written to, consider allowing the lint | ||
= note: `-D clippy::pointer-in-nomem-asm-block` implied by `-D warnings` | ||
= help: to override `-D warnings` add `#[allow(clippy::pointer_in_nomem_asm_block)]` | ||
|
||
error: passing pointer to nomem asm block | ||
--> tests/ui/pointer_in_nomem_asm_block.rs:26:22 | ||
| | ||
LL | asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); | ||
| ^^^^^^^^^^^^^ | ||
| | ||
= note: `nomem` means that no memory write or read happens inside them asm! block | ||
= note: if this is intentional and no pointers are read or written to, consider allowing the lint | ||
|
||
error: passing pointer to nomem asm block | ||
--> tests/ui/pointer_in_nomem_asm_block.rs:31:22 | ||
| | ||
LL | asm!("call {p}", p = in(reg) p, options(nomem)); | ||
| ^^^^^^^^^^^^^ | ||
| | ||
= note: `nomem` means that no memory write or read happens inside them asm! block | ||
= note: if this is intentional and no pointers are read or written to, consider allowing the lint | ||
|
||
error: aborting due to 3 previous errors | ||
|