Skip to content

Commit d590083

Browse files
committed
add lint for suspicious asm options
1 parent 86d348d commit d590083

File tree

5 files changed

+122
-0
lines changed

5 files changed

+122
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5714,6 +5714,7 @@ Released 2018-09-13
57145714
[`path_ends_with_ext`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_ends_with_ext
57155715
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
57165716
[`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false
5717+
[`pointer_in_nomem_asm_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointer_in_nomem_asm_block
57175718
[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
57185719
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
57195720
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
600600
crate::pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF_INFO,
601601
crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO,
602602
crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO,
603+
crate::pointer_in_nomem_asm_block::POINTER_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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ mod partialeq_to_none;
289289
mod pass_by_ref_or_value;
290290
mod pattern_type_mismatch;
291291
mod permissions_set_readonly_false;
292+
mod pointer_in_nomem_asm_block;
292293
mod precedence;
293294
mod ptr;
294295
mod ptr_offset_with_cast;
@@ -1176,6 +1177,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
11761177
store.register_late_pass(|_| Box::new(set_contains_or_insert::HashsetInsertAfterContains));
11771178
store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice));
11781179
store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest));
1180+
store.register_late_pass(|_| Box::new(pointer_in_nomem_asm_block::PointerInNomemAsmBlock));
11791181
// add lints here, do not remove this comment, it's used in `new_lint`
11801182
}
11811183

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
use rustc_hir::*;
2+
use rustc_lint::{LateContext, LateLintPass};
3+
use rustc_session::declare_lint_pass;
4+
use rustc_ast::InlineAsmOptions;
5+
use clippy_utils::diagnostics::span_lint;
6+
7+
#[derive(Clone, Copy, PartialEq, Eq)]
8+
enum AsmMemoryOptions {
9+
NoMem,
10+
Readonly,
11+
}
12+
13+
fn has_in_operand_pointer(cx: &LateContext<'_>, asm_op: &InlineAsmOperand<'_>) -> Option<Mutability> {
14+
let asm_in_expr = match asm_op {
15+
InlineAsmOperand::In { expr, .. } => expr,
16+
InlineAsmOperand::InOut { expr, .. } => expr,
17+
InlineAsmOperand::SplitInOut { in_expr, ..} => in_expr,
18+
InlineAsmOperand::SymStatic { .. } => return Some(Mutability::Not),
19+
InlineAsmOperand::Out { .. } => return None,
20+
InlineAsmOperand::Const { .. } => return None,
21+
InlineAsmOperand::SymFn { .. } => return None,
22+
InlineAsmOperand::Label { .. } => return None,
23+
};
24+
25+
return match cx.typeck_results().expr_ty(asm_in_expr).kind() {
26+
rustc_middle::ty::TyKind::RawPtr(_, mutbl) => Some(*mutbl),
27+
rustc_middle::ty::TyKind::Ref(_, _, mutbl) => Some(*mutbl),
28+
_ => None,
29+
};
30+
}
31+
32+
fn yeet_lint(cx: &LateContext<'_>, span: rustc_span::Span, msg: &'static str) {
33+
span_lint(cx, POINTER_IN_NOMEM_ASM_BLOCK, span, msg);
34+
}
35+
36+
fn check_asm(cx: &LateContext<'_>, asm: &InlineAsm<'_>) {
37+
let nomem = asm.options.contains(InlineAsmOptions::NOMEM);
38+
let rdonly = asm.options.contains(InlineAsmOptions::READONLY);
39+
let memopt = match (nomem, rdonly) {
40+
(false, false) => return, // nothing to check
41+
(false, true) => AsmMemoryOptions::Readonly,
42+
(true, false) => AsmMemoryOptions::NoMem,
43+
(true, true) => return, // just return, rustc should fail anyway
44+
};
45+
46+
for (asm_op, asm_op_span) in asm.operands {
47+
let p = match has_in_operand_pointer(cx, asm_op) {
48+
Some(p) => p,
49+
None => continue,
50+
};
51+
52+
let msg = match (p, memopt) {
53+
(Mutability::Not, AsmMemoryOptions::Readonly) => continue,
54+
(Mutability::Not, AsmMemoryOptions::NoMem) => "const pointer in nomem",
55+
(Mutability::Mut, AsmMemoryOptions::Readonly) => "mut pointer in readonly",
56+
(Mutability::Mut, AsmMemoryOptions::NoMem) => "mut pointer in nomem",
57+
};
58+
59+
yeet_lint(cx, *asm_op_span, msg);
60+
}
61+
}
62+
63+
declare_clippy_lint! {
64+
/// ### What it does
65+
///
66+
/// ### Why is this bad?
67+
///
68+
/// ### Example
69+
/// ```no_run
70+
/// // example code where clippy issues a warning
71+
/// ```
72+
/// Use instead:
73+
/// ```no_run
74+
/// // example code which does not raise clippy warning
75+
/// ```
76+
#[clippy::version = "1.81.0"]
77+
pub POINTER_IN_NOMEM_ASM_BLOCK,
78+
suspicious,
79+
"default lint description"
80+
}
81+
82+
declare_lint_pass!(PointerInNomemAsmBlock => [POINTER_IN_NOMEM_ASM_BLOCK]);
83+
84+
impl<'tcx> LateLintPass<'tcx> for PointerInNomemAsmBlock {
85+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
86+
if let ExprKind::InlineAsm(asm) = &expr.kind {
87+
check_asm(cx, asm);
88+
}
89+
}
90+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#![warn(clippy::pointer_in_nomem_asm_block)]
2+
#![crate_type = "lib"]
3+
#![no_std]
4+
5+
unsafe fn nomem_bad(p: &i32) {
6+
core::arch::asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
7+
}
8+
9+
unsafe fn readonly_bad(p: &mut i32) {
10+
core::arch::asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags));
11+
}
12+
13+
unsafe fn nomem_good(p: &i32) {
14+
core::arch::asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags));
15+
let p = p as *const i32 as usize;
16+
core::arch::asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
17+
}
18+
19+
unsafe fn readonly_good(p: &mut i32) {
20+
core::arch::asm!("asdf {p}", p = in(reg) p, options(nostack, preserves_flags));
21+
core::arch::asm!("asdf {p}", p = in(reg) &*p, options(readonly, nostack, preserves_flags));
22+
let p = p as *const i32;
23+
core::arch::asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags));
24+
}
25+
26+
unsafe fn nomem_bad2(p: &mut i32) {
27+
core::arch::asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
28+
}

0 commit comments

Comments
 (0)