Skip to content

Commit b00d875

Browse files
committed
add lint for inline asm labels that look like binary
1 parent c1b336c commit b00d875

File tree

8 files changed

+391
-91
lines changed

8 files changed

+391
-91
lines changed

compiler/rustc_lint/messages.ftl

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,6 @@ lint_builtin_allow_internal_unsafe =
5252
lint_builtin_anonymous_params = anonymous parameters are deprecated and will be removed in the next edition
5353
.suggestion = try naming the parameter or explicitly ignoring it
5454
55-
lint_builtin_asm_labels = avoid using named labels in inline assembly
56-
.help = only local labels of the form `<number>:` should be used in inline asm
57-
.note = see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
58-
5955
lint_builtin_box_pointers = type uses owned (Box type) pointers: {$ty}
6056
6157
lint_builtin_clashing_extern_diff_name = `{$this}` redeclares `{$orig}` with a different signature
@@ -401,6 +397,19 @@ lint_incomplete_include =
401397
402398
lint_inner_macro_attribute_unstable = inner macro attributes are unstable
403399
400+
lint_invalid_asm_label_binary = avoid using labels containing only the digits 0 and 1 in inline assembly
401+
.help = use a different number that has at least one digit 2 or greater
402+
.note = an LLVM bug makes these labels ambiguous with a binary literal number
403+
.note = see <https://bugs.llvm.org/show_bug.cgi?id=36144> for more information
404+
405+
lint_invalid_asm_label_format_arg = avoid using named labels in inline assembly
406+
.help = only local labels of the form `<number>:` should be used in inline asm
407+
.note1 = format arguments may expand to a non-numeric value
408+
.note2 = see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
409+
lint_invalid_asm_label_named = avoid using named labels in inline assembly
410+
.help = only local labels of the form `<number>:` should be used in inline asm
411+
.note = see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
412+
lint_invalid_asm_label_no_span = the label may be declared in the expansion of a macro
404413
lint_invalid_crate_type_value = invalid `crate_type` value
405414
.suggestion = did you mean
406415

compiler/rustc_lint/src/builtin.rs

Lines changed: 93 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ use crate::{
3030
BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures,
3131
BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents,
3232
BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
33-
BuiltinMutablesTransmutes, BuiltinNamedAsmLabel, BuiltinNoMangleGeneric,
34-
BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds,
35-
BuiltinTypeAliasGenericBounds, BuiltinTypeAliasGenericBoundsSuggestion,
36-
BuiltinTypeAliasWhereClause, BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit,
33+
BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
34+
BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds,
35+
BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause,
36+
BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit,
3737
BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe,
3838
BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
39-
BuiltinWhileTrue, SuggestChangingAssocTypes,
39+
BuiltinWhileTrue, InvalidAsmLabel, SuggestChangingAssocTypes,
4040
},
4141
EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
4242
};
@@ -45,7 +45,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
4545
use rustc_ast::visit::{FnCtxt, FnKind};
4646
use rustc_ast::{self as ast, *};
4747
use rustc_ast_pretty::pprust::{self, expr_to_string};
48-
use rustc_errors::{Applicability, LintDiagnostic, MultiSpan};
48+
use rustc_errors::{Applicability, LintDiagnostic};
4949
use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability};
5050
use rustc_hir as hir;
5151
use rustc_hir::def::{DefKind, Res};
@@ -70,7 +70,6 @@ use rustc_target::abi::Abi;
7070
use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
7171
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
7272
use rustc_trait_selection::traits::{self, misc::type_allowed_to_implement_copy};
73-
use tracing::debug;
7473

7574
use crate::nonstandard_style::{method_context, MethodLateContext};
7675

@@ -2804,10 +2803,48 @@ declare_lint! {
28042803
"named labels in inline assembly",
28052804
}
28062805

2807-
declare_lint_pass!(NamedAsmLabels => [NAMED_ASM_LABELS]);
2806+
declare_lint! {
2807+
/// The `binary_asm_labels` lint detects the use of numeric labels containing only binary
2808+
/// digits in the inline `asm!` macro.
2809+
///
2810+
/// ### Example
2811+
///
2812+
/// ```rust,compile_fail
2813+
/// # #![feature(asm_experimental_arch)]
2814+
/// use std::arch::asm;
2815+
///
2816+
/// fn main() {
2817+
/// unsafe {
2818+
/// asm!("0: bar");
2819+
/// }
2820+
/// }
2821+
/// ```
2822+
///
2823+
/// {{produces}}
2824+
///
2825+
/// ### Explanation
2826+
///
2827+
/// A bug in LLVM causes this code to fail
2828+
///
2829+
/// See the explanation in [Rust By Example] for more details.
2830+
///
2831+
/// [local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels
2832+
/// [Rust By Example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels
2833+
pub BINARY_ASM_LABELS,
2834+
Deny,
2835+
"labels in inline assembly containing only 0 or 1 digits",
2836+
}
2837+
2838+
declare_lint_pass!(AsmLabels => [NAMED_ASM_LABELS, BINARY_ASM_LABELS]);
2839+
2840+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2841+
enum AsmLabelKind {
2842+
Named,
2843+
FormatArg,
2844+
Binary,
2845+
}
28082846

2809-
impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
2810-
#[allow(rustc::diagnostic_outside_of_impl)]
2847+
impl<'tcx> LateLintPass<'tcx> for AsmLabels {
28112848
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
28122849
if let hir::Expr {
28132850
kind: hir::ExprKind::InlineAsm(hir::InlineAsm { template_strs, options, .. }),
@@ -2835,7 +2872,8 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
28352872
None
28362873
};
28372874

2838-
let mut found_labels = Vec::new();
2875+
// diagnostics are emitted per-template, so this is created here as opposed to the outer loop
2876+
let mut spans = Vec::new();
28392877

28402878
// A semicolon might not actually be specified as a separator for all targets, but
28412879
// it seems like LLVM accepts it always.
@@ -2858,16 +2896,21 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
28582896

28592897
// Whether a { bracket has been seen and its } hasn't been found yet.
28602898
let mut in_bracket = false;
2899+
let mut label_kind = AsmLabelKind::Named;
28612900

2862-
// A label starts with an ASCII alphabetic character or . or _
28632901
// A label can also start with a format arg, if it's not a raw asm block.
28642902
if !raw && start == '{' {
28652903
in_bracket = true;
2904+
label_kind = AsmLabelKind::FormatArg;
2905+
} else if matches!(start, '0' | '1') {
2906+
// binary labels have only the characters 0 or 1
2907+
label_kind = AsmLabelKind::Binary;
28662908
} else if !(start.is_ascii_alphabetic() || matches!(start, '.' | '_')) {
2909+
// named labels start with ASCII letters or . or _
2910+
// anything else is not a label
28672911
break 'label_loop;
28682912
}
28692913

2870-
// Labels continue with ASCII alphanumeric characters, _, or $
28712914
for c in chars {
28722915
// Inside a template format arg, any character is permitted for the
28732916
// puproses of label detection because we assume that it can be
@@ -2888,34 +2931,55 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
28882931
} else if !raw && c == '{' {
28892932
// Start of a format arg.
28902933
in_bracket = true;
2934+
label_kind = AsmLabelKind::FormatArg;
28912935
} else {
2892-
if !(c.is_ascii_alphanumeric() || matches!(c, '_' | '$')) {
2936+
let can_continue = match label_kind {
2937+
// format arg labels are considered to be named labels for the purposes
2938+
// of continuing outside of their {} pair.
2939+
AsmLabelKind::Named | AsmLabelKind::FormatArg => {
2940+
c.is_ascii_alphanumeric() || matches!(c, '_' | '$')
2941+
}
2942+
AsmLabelKind::Binary => matches!(c, '0' | '1'),
2943+
};
2944+
2945+
if !can_continue {
28932946
// The potential label had an invalid character inside it, it
28942947
// cannot be a label.
28952948
break 'label_loop;
28962949
}
28972950
}
28982951
}
28992952

2900-
// If all characters passed the label checks, this is likely a label.
2901-
found_labels.push(possible_label);
2953+
// If all characters passed the label checks, this is a label.
2954+
spans.push((find_label_span(possible_label), label_kind));
29022955
start_idx = idx + 1;
29032956
}
29042957
}
29052958

2906-
debug!("NamedAsmLabels::check_expr(): found_labels: {:#?}", &found_labels);
2907-
2908-
if found_labels.len() > 0 {
2909-
let spans = found_labels
2910-
.into_iter()
2911-
.filter_map(|label| find_label_span(label))
2912-
.collect::<Vec<Span>>();
2913-
// If there were labels but we couldn't find a span, combine the warnings and
2914-
// use the template span.
2915-
let target_spans: MultiSpan =
2916-
if spans.len() > 0 { spans.into() } else { (*template_span).into() };
2917-
2918-
cx.emit_span_lint(NAMED_ASM_LABELS, target_spans, BuiltinNamedAsmLabel);
2959+
for (span, label_kind) in spans {
2960+
let missing_precise_span = span.is_none();
2961+
let span = span.unwrap_or(*template_span);
2962+
match label_kind {
2963+
AsmLabelKind::Named => {
2964+
cx.emit_span_lint(
2965+
NAMED_ASM_LABELS,
2966+
span,
2967+
InvalidAsmLabel::Named { missing_precise_span },
2968+
);
2969+
}
2970+
AsmLabelKind::FormatArg => {
2971+
cx.emit_span_lint(
2972+
NAMED_ASM_LABELS,
2973+
span,
2974+
InvalidAsmLabel::FormatArg { missing_precise_span },
2975+
);
2976+
}
2977+
AsmLabelKind::Binary => cx.emit_span_lint(
2978+
BINARY_ASM_LABELS,
2979+
span,
2980+
InvalidAsmLabel::Binary { missing_precise_span },
2981+
),
2982+
};
29192983
}
29202984
}
29212985
}

compiler/rustc_lint/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ late_lint_methods!(
221221
NoopMethodCall: NoopMethodCall,
222222
EnumIntrinsicsNonEnums: EnumIntrinsicsNonEnums,
223223
InvalidAtomicOrdering: InvalidAtomicOrdering,
224-
NamedAsmLabels: NamedAsmLabels,
224+
AsmLabels: AsmLabels,
225225
OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
226226
MultipleSupertraitUpcastable: MultipleSupertraitUpcastable,
227227
MapUnitFn: MapUnitFn,

compiler/rustc_lint/src/lints.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2046,10 +2046,30 @@ pub struct UnitBindingsDiag {
20462046
}
20472047

20482048
#[derive(LintDiagnostic)]
2049-
#[diag(lint_builtin_asm_labels)]
2050-
#[help]
2051-
#[note]
2052-
pub struct BuiltinNamedAsmLabel;
2049+
pub enum InvalidAsmLabel {
2050+
#[diag(lint_invalid_asm_label_named)]
2051+
#[help]
2052+
#[note]
2053+
Named {
2054+
#[note(lint_invalid_asm_label_no_span)]
2055+
missing_precise_span: bool,
2056+
},
2057+
#[diag(lint_invalid_asm_label_format_arg)]
2058+
#[help]
2059+
#[note(lint_note1)]
2060+
#[note(lint_note2)]
2061+
FormatArg {
2062+
#[note(lint_invalid_asm_label_no_span)]
2063+
missing_precise_span: bool,
2064+
},
2065+
#[diag(lint_invalid_asm_label_binary)]
2066+
#[help]
2067+
#[note]
2068+
Binary {
2069+
#[note(lint_invalid_asm_label_no_span)]
2070+
missing_precise_span: bool,
2071+
},
2072+
}
20532073

20542074
#[derive(Subdiagnostic)]
20552075
pub enum UnexpectedCfgCargoHelp {

tests/ui/asm/binary_asm_labels.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//@ needs-asm-support
2+
//@ only-x86_64
3+
4+
// tests that labels containing only the digits 0 and 1 are rejected
5+
// uses of such labels can sometimes be interpreted as a binary literal
6+
7+
use std::arch::{asm, global_asm};
8+
9+
fn main() {
10+
unsafe {
11+
asm!("0: jmp 0b"); //~ ERROR avoid using labels containing only the digits
12+
asm!("1: jmp 1b"); //~ ERROR avoid using labels containing only the digits
13+
asm!("10: jmp 10b"); //~ ERROR avoid using labels containing only the digits
14+
asm!("01: jmp 01b"); //~ ERROR avoid using labels containing only the digits
15+
asm!("1001101: jmp 1001101b"); //~ ERROR avoid using labels containing only the digits
16+
}
17+
}

tests/ui/asm/binary_asm_labels.stderr

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
error: avoid using labels containing only the digits 0 and 1 in inline assembly
2+
--> $DIR/binary_asm_labels.rs:11:15
3+
|
4+
LL | asm!("0: jmp 0b");
5+
| ^
6+
|
7+
= help: use a different number that has at least one digit 2 or greater
8+
= note: an LLVM bug makes these labels ambiguous with a binary literal number
9+
= note: `#[deny(binary_asm_labels)]` on by default
10+
11+
error: avoid using labels containing only the digits 0 and 1 in inline assembly
12+
--> $DIR/binary_asm_labels.rs:12:15
13+
|
14+
LL | asm!("1: jmp 1b");
15+
| ^
16+
|
17+
= help: use a different number that has at least one digit 2 or greater
18+
= note: an LLVM bug makes these labels ambiguous with a binary literal number
19+
20+
error: avoid using labels containing only the digits 0 and 1 in inline assembly
21+
--> $DIR/binary_asm_labels.rs:13:15
22+
|
23+
LL | asm!("10: jmp 10b");
24+
| ^^
25+
|
26+
= help: use a different number that has at least one digit 2 or greater
27+
= note: an LLVM bug makes these labels ambiguous with a binary literal number
28+
29+
error: avoid using labels containing only the digits 0 and 1 in inline assembly
30+
--> $DIR/binary_asm_labels.rs:14:15
31+
|
32+
LL | asm!("01: jmp 01b");
33+
| ^^
34+
|
35+
= help: use a different number that has at least one digit 2 or greater
36+
= note: an LLVM bug makes these labels ambiguous with a binary literal number
37+
38+
error: avoid using labels containing only the digits 0 and 1 in inline assembly
39+
--> $DIR/binary_asm_labels.rs:15:15
40+
|
41+
LL | asm!("1001101: jmp 1001101b");
42+
| ^^^^^^^
43+
|
44+
= help: use a different number that has at least one digit 2 or greater
45+
= note: an LLVM bug makes these labels ambiguous with a binary literal number
46+
47+
error: aborting due to 5 previous errors
48+

0 commit comments

Comments
 (0)