Skip to content

Commit ba8106e

Browse files
committed
Warn when passing pointers to asm! with nomem/readonly options
1 parent 2495953 commit ba8106e

File tree

3 files changed

+73
-24
lines changed

3 files changed

+73
-24
lines changed

compiler/rustc_hir_analysis/src/check/intrinsicck.rs

+26-24
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_ast::InlineAsmTemplatePiece;
1+
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
22
use rustc_data_structures::fx::FxIndexSet;
33
use rustc_hir::{self as hir, LangItem};
44
use rustc_middle::bug;
@@ -124,7 +124,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
124124
idx: usize,
125125
reg: InlineAsmRegOrRegClass,
126126
expr: &'tcx hir::Expr<'tcx>,
127-
template: &[InlineAsmTemplatePiece],
127+
asm: &hir::InlineAsm<'tcx>,
128128
is_input: bool,
129129
tied_input: Option<(&'tcx hir::Expr<'tcx>, Option<InlineAsmType>)>,
130130
target_features: &FxIndexSet<Symbol>,
@@ -267,7 +267,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
267267
// Search for any use of this operand without a modifier and emit
268268
// the suggestion for them.
269269
let mut spans = vec![];
270-
for piece in template {
270+
for piece in asm.template {
271271
if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece
272272
{
273273
if operand_idx == idx && modifier.is_none() {
@@ -299,6 +299,24 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
299299
}
300300
}
301301

302+
if let ty::RawPtr(_, mutability) = *ty.kind() {
303+
let is_mut = match mutability {
304+
hir::Mutability::Not => false,
305+
hir::Mutability::Mut => true,
306+
};
307+
308+
if is_mut && asm.options.contains(InlineAsmOptions::READONLY) {
309+
let msg = "Passing a mutable pointer to asm!() with 'readonly' option";
310+
let note = "Mutable pointer suggest that this piece of assembly modifies the underlying object, consider using const pointer or checking the options";
311+
self.tcx.dcx().struct_span_warn(expr.span, msg).with_note(note).emit();
312+
}
313+
if asm.options.contains(InlineAsmOptions::NOMEM) {
314+
let msg = "Passing a pointer to asm!() with 'nomem' option";
315+
let note = "Pointer suggest that this piece of assembly reads the underlying object, consider using usize or checking the options";
316+
self.tcx.dcx().struct_span_warn(expr.span, msg).with_note(note).emit();
317+
}
318+
}
319+
302320
Some(asm_ty)
303321
}
304322

@@ -399,46 +417,30 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
399417

400418
match *op {
401419
hir::InlineAsmOperand::In { reg, expr } => {
402-
self.check_asm_operand_type(
403-
idx,
404-
reg,
405-
expr,
406-
asm.template,
407-
true,
408-
None,
409-
target_features,
410-
);
420+
self.check_asm_operand_type(idx, reg, expr, asm, true, None, target_features);
411421
}
412422
hir::InlineAsmOperand::Out { reg, late: _, expr } => {
413423
if let Some(expr) = expr {
414424
self.check_asm_operand_type(
415425
idx,
416426
reg,
417427
expr,
418-
asm.template,
428+
asm,
419429
false,
420430
None,
421431
target_features,
422432
);
423433
}
424434
}
425435
hir::InlineAsmOperand::InOut { reg, late: _, expr } => {
426-
self.check_asm_operand_type(
427-
idx,
428-
reg,
429-
expr,
430-
asm.template,
431-
false,
432-
None,
433-
target_features,
434-
);
436+
self.check_asm_operand_type(idx, reg, expr, asm, false, None, target_features);
435437
}
436438
hir::InlineAsmOperand::SplitInOut { reg, late: _, in_expr, out_expr } => {
437439
let in_ty = self.check_asm_operand_type(
438440
idx,
439441
reg,
440442
in_expr,
441-
asm.template,
443+
asm,
442444
true,
443445
None,
444446
target_features,
@@ -448,7 +450,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
448450
idx,
449451
reg,
450452
out_expr,
451-
asm.template,
453+
asm,
452454
false,
453455
Some((in_expr, in_ty)),
454456
target_features,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//@ only-x86_64
2+
//@ needs-asm-support
3+
//@ build-pass
4+
5+
#![crate_type = "lib"]
6+
#![no_std]
7+
8+
unsafe fn nomem_bad(p: &i32) {
9+
core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
10+
//~^ WARNING Passing a pointer to asm!() with 'nomem' option
11+
}
12+
13+
unsafe fn readonly_bad(p: &mut i32) {
14+
core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(readonly, nostack, preserves_flags));
15+
//~^ WARNING Passing a mutable pointer to asm!() with 'readonly' option
16+
}
17+
18+
unsafe fn nomem_good(p: &i32) {
19+
core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(readonly, nostack, preserves_flags));
20+
let p = p as *const i32 as usize;
21+
core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
22+
}
23+
24+
unsafe fn readonly_good(p: &mut i32) {
25+
core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(nostack, preserves_flags));
26+
core::arch::asm!("mov {p}, {p}", p = in(reg) &*p, options(readonly, nostack, preserves_flags));
27+
let p = p as *const i32;
28+
core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(readonly, nostack, preserves_flags));
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
warning: Passing a pointer to asm!() with 'nomem' option
2+
--> $DIR/passing-pointer-nomem-readonly.rs:9:50
3+
|
4+
LL | core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
5+
| ^
6+
|
7+
= note: Pointer suggest that this piece of assembly reads the underlying object, consider using usize or checking the options
8+
9+
warning: Passing a mutable pointer to asm!() with 'readonly' option
10+
--> $DIR/passing-pointer-nomem-readonly.rs:14:50
11+
|
12+
LL | core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(readonly, nostack, preserves_flags));
13+
| ^
14+
|
15+
= note: Mutable pointer suggest that this piece of assembly modifies the underlying object, consider using const pointer or checking the options
16+
17+
warning: 2 warnings emitted
18+

0 commit comments

Comments
 (0)