-
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.
Auto merge of #13247 - Soveu:sus-asm-options, r=y21
Add new check for passing pointers to an `asm!` block with `nomem` option changelog: Add new check for passing pointers to an `asm!` block with `nomem` option Continuing work from rust-lang/rust#127063
- 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 POINTERS_IN_NOMEM_ASM_BLOCK, | ||
suspicious, | ||
"pointers in nomem asm block" | ||
} | ||
|
||
declare_lint_pass!(PointersInNomemAsmBlock => [POINTERS_IN_NOMEM_ASM_BLOCK]); | ||
|
||
impl<'tcx> LateLintPass<'tcx> for PointersInNomemAsmBlock { | ||
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, | ||
POINTERS_IN_NOMEM_ASM_BLOCK, | ||
spans, | ||
"passing pointers 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 the 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::pointers_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 pointers 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 pointers to nomem asm block | ||
} | ||
|
||
unsafe fn nomem_fn(p: extern "C" fn()) { | ||
asm!("call {p}", p = in(reg) p, options(nomem)); | ||
//~^ ERROR: passing pointers 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 pointers to nomem asm block | ||
--> tests/ui/pointers_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 the asm! block | ||
= note: if this is intentional and no pointers are read or written to, consider allowing the lint | ||
= note: `-D clippy::pointers-in-nomem-asm-block` implied by `-D warnings` | ||
= help: to override `-D warnings` add `#[allow(clippy::pointers_in_nomem_asm_block)]` | ||
|
||
error: passing pointers to nomem asm block | ||
--> tests/ui/pointers_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 the asm! block | ||
= note: if this is intentional and no pointers are read or written to, consider allowing the lint | ||
|
||
error: passing pointers to nomem asm block | ||
--> tests/ui/pointers_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 the 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 | ||
|