Skip to content

Commit 49f31ff

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

File tree

3 files changed

+96
-24
lines changed

3 files changed

+96
-24
lines changed

compiler/rustc_hir_analysis/src/check/intrinsicck.rs

+30-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,28 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
299299
}
300300
}
301301

302+
match *ty.kind() {
303+
ty::RawPtr(_, hir::Mutability::Mut) if asm.options.contains(InlineAsmOptions::READONLY) =>
304+
self
305+
.tcx
306+
.dcx()
307+
.struct_span_warn(expr.span, "passing a mutable pointer to asm! block with 'readonly' option.")
308+
.with_note("`readonly` means that no memory write happens inside the asm! block.")
309+
.with_note("This is not limited to global variables, it also includes passed pointers.")
310+
.with_note("If passing this mutable pointer is intentional, remove the `readonly` attribute.")
311+
.emit(),
312+
ty::RawPtr(_, _) if asm.options.contains(InlineAsmOptions::NOMEM) =>
313+
self
314+
.tcx
315+
.dcx()
316+
.struct_span_warn(expr.span, "passing a pointer to asm! block with 'nomem' option.")
317+
.with_note("`nomem` means that no memory write or read happens inside the asm! block.")
318+
.with_note("This is not limited to global variables, it also includes passed pointers.")
319+
.with_note("If passing this pointer is intentional, replace the `nomem` attribute with `readonly` or remove it completely.")
320+
.emit(),
321+
_ => {} // we're only interested in pointers when asm! has `nomem` or `readonly`
322+
}
323+
302324
Some(asm_ty)
303325
}
304326

@@ -399,46 +421,30 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
399421

400422
match *op {
401423
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-
);
424+
self.check_asm_operand_type(idx, reg, expr, asm, true, None, target_features);
411425
}
412426
hir::InlineAsmOperand::Out { reg, late: _, expr } => {
413427
if let Some(expr) = expr {
414428
self.check_asm_operand_type(
415429
idx,
416430
reg,
417431
expr,
418-
asm.template,
432+
asm,
419433
false,
420434
None,
421435
target_features,
422436
);
423437
}
424438
}
425439
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-
);
440+
self.check_asm_operand_type(idx, reg, expr, asm, false, None, target_features);
435441
}
436442
hir::InlineAsmOperand::SplitInOut { reg, late: _, in_expr, out_expr } => {
437443
let in_ty = self.check_asm_operand_type(
438444
idx,
439445
reg,
440446
in_expr,
441-
asm.template,
447+
asm,
442448
true,
443449
None,
444450
target_features,
@@ -448,7 +454,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
448454
idx,
449455
reg,
450456
out_expr,
451-
asm.template,
457+
asm,
452458
false,
453459
Some((in_expr, in_ty)),
454460
target_features,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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! block 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! block 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+
}
30+
31+
unsafe fn nomem_bad2(p: &mut i32) {
32+
core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
33+
//~^ WARNING passing a pointer to asm! block with 'nomem' option.
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
warning: passing a pointer to asm! block 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: `nomem` means that no memory write or read happens inside the asm! block.
8+
= note: This is not limited to global variables, it also includes passed pointers.
9+
= note: If passing this pointer is intentional, replace the `nomem` attribute with `readonly` or remove it completely.
10+
11+
warning: passing a mutable pointer to asm! block with 'readonly' option.
12+
--> $DIR/passing-pointer-nomem-readonly.rs:14:50
13+
|
14+
LL | core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(readonly, nostack, preserves_flags));
15+
| ^
16+
|
17+
= note: `readonly` means that no memory write happens inside the asm! block.
18+
= note: This is not limited to global variables, it also includes passed pointers.
19+
= note: If passing this mutable pointer is intentional, remove the `readonly` attribute.
20+
21+
warning: passing a pointer to asm! block with 'nomem' option.
22+
--> $DIR/passing-pointer-nomem-readonly.rs:32:50
23+
|
24+
LL | core::arch::asm!("mov {p}, {p}", p = in(reg) p, options(nomem, nostack, preserves_flags));
25+
| ^
26+
|
27+
= note: `nomem` means that no memory write or read happens inside the asm! block.
28+
= note: This is not limited to global variables, it also includes passed pointers.
29+
= note: If passing this pointer is intentional, replace the `nomem` attribute with `readonly` or remove it completely.
30+
31+
warning: 3 warnings emitted
32+

0 commit comments

Comments
 (0)