Skip to content

Commit 273b561

Browse files
committed
add pointers_in_nomem_asm_block lint
1 parent a81f1c8 commit 273b561

6 files changed

+159
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5758,6 +5758,7 @@ Released 2018-09-13
57585758
[`pathbuf_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#pathbuf_init_then_push
57595759
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
57605760
[`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false
5761+
[`pointers_in_nomem_asm_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointers_in_nomem_asm_block
57615762
[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
57625763
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
57635764
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
600600
crate::pathbuf_init_then_push::PATHBUF_INIT_THEN_PUSH_INFO,
601601
crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO,
602602
crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO,
603+
crate::pointers_in_nomem_asm_block::POINTERS_IN_NOMEM_ASM_BLOCK_INFO,
603604
crate::precedence::PRECEDENCE_INFO,
604605
crate::ptr::CMP_NULL_INFO,
605606
crate::ptr::INVALID_NULL_PTR_USAGE_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ mod pass_by_ref_or_value;
287287
mod pathbuf_init_then_push;
288288
mod pattern_type_mismatch;
289289
mod permissions_set_readonly_false;
290+
mod pointers_in_nomem_asm_block;
290291
mod precedence;
291292
mod ptr;
292293
mod ptr_offset_with_cast;
@@ -935,5 +936,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
935936
store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice));
936937
store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest));
937938
store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses));
939+
store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock));
938940
// add lints here, do not remove this comment, it's used in `new_lint`
939941
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use rustc_ast::InlineAsmOptions;
3+
use rustc_hir::{Expr, ExprKind, InlineAsm, InlineAsmOperand};
4+
use rustc_lint::{LateContext, LateLintPass};
5+
use rustc_session::declare_lint_pass;
6+
use rustc_span::Span;
7+
8+
declare_clippy_lint! {
9+
/// ### What it does
10+
/// Checks if any pointer is being passed to an asm! block with `nomem` option.
11+
///
12+
/// ### Why is this bad?
13+
/// `nomem` forbids any reads or writes to memory and passing a pointer suggests
14+
/// that either of those will happen.
15+
///
16+
/// ### Example
17+
/// ```no_run
18+
/// fn f(p: *mut u32) {
19+
/// unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nomem, nostack)); }
20+
/// }
21+
/// ```
22+
/// Use instead:
23+
/// ```no_run
24+
/// fn f(p: *mut u32) {
25+
/// unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nostack)); }
26+
/// }
27+
/// ```
28+
#[clippy::version = "1.81.0"]
29+
pub POINTERS_IN_NOMEM_ASM_BLOCK,
30+
suspicious,
31+
"pointers in nomem asm block"
32+
}
33+
34+
declare_lint_pass!(PointersInNomemAsmBlock => [POINTERS_IN_NOMEM_ASM_BLOCK]);
35+
36+
impl<'tcx> LateLintPass<'tcx> for PointersInNomemAsmBlock {
37+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
38+
if let ExprKind::InlineAsm(asm) = &expr.kind {
39+
check_asm(cx, asm);
40+
}
41+
}
42+
}
43+
44+
fn check_asm(cx: &LateContext<'_>, asm: &InlineAsm<'_>) {
45+
if !asm.options.contains(InlineAsmOptions::NOMEM) {
46+
return;
47+
}
48+
49+
let spans = asm
50+
.operands
51+
.iter()
52+
.filter(|(op, _span)| has_in_operand_pointer(cx, op))
53+
.map(|(_op, span)| *span)
54+
.collect::<Vec<Span>>();
55+
56+
if spans.is_empty() {
57+
return;
58+
}
59+
60+
span_lint_and_then(
61+
cx,
62+
POINTERS_IN_NOMEM_ASM_BLOCK,
63+
spans,
64+
"passing pointers to nomem asm block",
65+
additional_notes,
66+
);
67+
}
68+
69+
fn has_in_operand_pointer(cx: &LateContext<'_>, asm_op: &InlineAsmOperand<'_>) -> bool {
70+
let asm_in_expr = match asm_op {
71+
InlineAsmOperand::SymStatic { .. }
72+
| InlineAsmOperand::Out { .. }
73+
| InlineAsmOperand::Const { .. }
74+
| InlineAsmOperand::SymFn { .. }
75+
| InlineAsmOperand::Label { .. } => return false,
76+
InlineAsmOperand::SplitInOut { in_expr, .. } => in_expr,
77+
InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => expr,
78+
};
79+
80+
// This checks for raw ptrs, refs and function pointers - the last one
81+
// also technically counts as reading memory.
82+
cx.typeck_results().expr_ty(asm_in_expr).is_any_ptr()
83+
}
84+
85+
fn additional_notes(diag: &mut rustc_errors::Diag<'_, ()>) {
86+
diag.note("`nomem` means that no memory write or read happens inside the asm! block");
87+
diag.note("if this is intentional and no pointers are read or written to, consider allowing the lint");
88+
}
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//@ needs-asm-support
2+
#![warn(clippy::pointers_in_nomem_asm_block)]
3+
#![crate_type = "lib"]
4+
#![no_std]
5+
6+
use core::arch::asm;
7+
8+
unsafe fn nomem_bad(p: &i32) {
9+
asm!(
10+
"asdf {p1}, {p2}, {p3}",
11+
p1 = in(reg) p,
12+
//~^ ERROR: passing pointers to nomem asm block
13+
p2 = in(reg) p as *const _ as usize,
14+
p3 = in(reg) p,
15+
options(nomem, nostack, preserves_flags)
16+
);
17+
}
18+
19+
unsafe fn nomem_good(p: &i32) {
20+
asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags));
21+
let p = p as *const i32 as usize;
22+
asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
23+
}
24+
25+
unsafe fn nomem_bad2(p: &mut i32) {
26+
asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
27+
//~^ ERROR: passing pointers to nomem asm block
28+
}
29+
30+
unsafe fn nomem_fn(p: extern "C" fn()) {
31+
asm!("call {p}", p = in(reg) p, options(nomem));
32+
//~^ ERROR: passing pointers to nomem asm block
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
error: passing pointers to nomem asm block
2+
--> tests/ui/pointers_in_nomem_asm_block.rs:11:9
3+
|
4+
LL | p1 = in(reg) p,
5+
| ^^^^^^^^^^^^^^
6+
...
7+
LL | p3 = in(reg) p,
8+
| ^^^^^^^^^^^^^^
9+
|
10+
= note: `nomem` means that no memory write or read happens inside the asm! block
11+
= note: if this is intentional and no pointers are read or written to, consider allowing the lint
12+
= note: `-D clippy::pointers-in-nomem-asm-block` implied by `-D warnings`
13+
= help: to override `-D warnings` add `#[allow(clippy::pointers_in_nomem_asm_block)]`
14+
15+
error: passing pointers to nomem asm block
16+
--> tests/ui/pointers_in_nomem_asm_block.rs:26:22
17+
|
18+
LL | asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
19+
| ^^^^^^^^^^^^^
20+
|
21+
= note: `nomem` means that no memory write or read happens inside the asm! block
22+
= note: if this is intentional and no pointers are read or written to, consider allowing the lint
23+
24+
error: passing pointers to nomem asm block
25+
--> tests/ui/pointers_in_nomem_asm_block.rs:31:22
26+
|
27+
LL | asm!("call {p}", p = in(reg) p, options(nomem));
28+
| ^^^^^^^^^^^^^
29+
|
30+
= note: `nomem` means that no memory write or read happens inside the asm! block
31+
= note: if this is intentional and no pointers are read or written to, consider allowing the lint
32+
33+
error: aborting due to 3 previous errors
34+

0 commit comments

Comments
 (0)