Skip to content

Commit d623ec6

Browse files
committed
Auto merge of #54568 - levex:issue-54130, r=nagisa
codegen_llvm: check inline assembly constraints with LLVM ---%<--- Hey all, As issue #54130 highlights, constraints are not checked and passing bad constraints to LLVM can crash it since a `Verify()` call is placed inside an assertion (see: `src/llvm/lib/IR/InlineAsm.cpp:39`). As this is my first PR to the Rust compiler (woot! 🎉), there might be better ways of achieving this result. In particular, I am not too happy about generating an error in codegen; it would be much nicer if we did it earlier. However, @rkruppe [noted on IRC](https://botbot.me/mozilla/rustc/2018-09-25/?msg=104791581&page=1) that this should be fine for an unstable feature and a much better solution than the _status quo_, which is an ICE. Thanks! --->%--- LLVM provides a way of checking whether the constraints and the actual inline assembly make sense. This commit introduces a check before emitting code for the inline assembly. If LLVM rejects the inline assembly (or its constraints), then the compiler emits an error E0668 ("malformed inline assembly"). Fixes: #54130 Signed-off-by: Levente Kurusa \<[email protected]\>
2 parents bd8d030 + 0991d20 commit d623ec6

File tree

8 files changed

+122
-6
lines changed

8 files changed

+122
-6
lines changed

Diff for: src/librustc_codegen_llvm/asm.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub fn codegen_inline_asm(
3030
ia: &hir::InlineAsm,
3131
outputs: Vec<PlaceRef<'ll, 'tcx>>,
3232
mut inputs: Vec<&'ll Value>
33-
) {
33+
) -> bool {
3434
let mut ext_constraints = vec![];
3535
let mut output_types = vec![];
3636

@@ -97,6 +97,10 @@ pub fn codegen_inline_asm(
9797
ia.alignstack,
9898
dialect
9999
);
100+
if r.is_none() {
101+
return false;
102+
}
103+
let r = r.unwrap();
100104

101105
// Again, based on how many outputs we have
102106
let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect);
@@ -117,6 +121,8 @@ pub fn codegen_inline_asm(
117121
llvm::LLVMSetMetadata(r, kind,
118122
llvm::LLVMMDNodeInContext(bx.cx.llcx, &val, 1));
119123
}
124+
125+
return true;
120126
}
121127

122128
pub fn codegen_global_asm<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,

Diff for: src/librustc_codegen_llvm/builder.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,7 @@ impl Builder<'a, 'll, 'tcx> {
737737
pub fn inline_asm_call(&self, asm: *const c_char, cons: *const c_char,
738738
inputs: &[&'ll Value], output: &'ll Type,
739739
volatile: bool, alignstack: bool,
740-
dia: AsmDialect) -> &'ll Value {
740+
dia: AsmDialect) -> Option<&'ll Value> {
741741
self.count_insn("inlineasm");
742742

743743
let volatile = if volatile { llvm::True }
@@ -753,9 +753,17 @@ impl Builder<'a, 'll, 'tcx> {
753753
debug!("Asm Output Type: {:?}", output);
754754
let fty = Type::func(&argtys[..], output);
755755
unsafe {
756-
let v = llvm::LLVMRustInlineAsm(
757-
fty, asm, cons, volatile, alignstack, dia);
758-
self.call(v, inputs, None)
756+
// Ask LLVM to verify that the constraints are well-formed.
757+
let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons);
758+
debug!("Constraint verification result: {:?}", constraints_ok);
759+
if constraints_ok == llvm::True {
760+
let v = llvm::LLVMRustInlineAsm(
761+
fty, asm, cons, volatile, alignstack, dia);
762+
Some(self.call(v, inputs, None))
763+
} else {
764+
// LLVM has detected an issue with our constaints, bail out
765+
None
766+
}
759767
}
760768
}
761769

Diff for: src/librustc_codegen_llvm/diagnostics.rs

+22
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,26 @@ unsafe { simd_add(i32x2(0, 0), i32x2(1, 2)); } // ok!
4747
```
4848
"##,
4949

50+
E0668: r##"
51+
Malformed inline assembly rejected by LLVM.
52+
53+
LLVM checks the validity of the constraints and the assembly string passed to
54+
it. This error implies that LLVM seems something wrong with the inline
55+
assembly call.
56+
57+
In particular, it can happen if you forgot the closing bracket of a register
58+
constraint (see issue #51430):
59+
```ignore (error-emitted-at-codegen-which-cannot-be-handled-by-compile_fail)
60+
#![feature(asm)]
61+
62+
fn main() {
63+
let rax: u64;
64+
unsafe {
65+
asm!("" :"={rax"(rax));
66+
println!("Accumulator is: {}", rax);
67+
}
68+
}
69+
```
70+
"##,
71+
5072
}

Diff for: src/librustc_codegen_llvm/llvm/ffi.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,9 @@ extern "C" {
12081208
AlignStack: Bool,
12091209
Dialect: AsmDialect)
12101210
-> &Value;
1211+
pub fn LLVMRustInlineAsmVerify(Ty: &Type,
1212+
Constraints: *const c_char)
1213+
-> Bool;
12111214

12121215
pub fn LLVMRustDebugMetadataVersion() -> u32;
12131216
pub fn LLVMRustVersionMajor() -> u32;

Diff for: src/librustc_codegen_llvm/mir/statement.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,11 @@ impl FunctionCx<'a, 'll, 'tcx> {
8686
self.codegen_operand(&bx, input).immediate()
8787
}).collect();
8888

89-
asm::codegen_inline_asm(&bx, asm, outputs, input_vals);
89+
let res = asm::codegen_inline_asm(&bx, asm, outputs, input_vals);
90+
if !res {
91+
span_err!(bx.sess(), statement.source_info.span, E0668,
92+
"malformed inline assembly");
93+
}
9094
bx
9195
}
9296
mir::StatementKind::FakeRead(..) |

Diff for: src/rustllvm/RustWrapper.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,11 @@ extern "C" LLVMValueRef LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString,
426426
HasSideEffects, IsAlignStack, fromRust(Dialect)));
427427
}
428428

429+
extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty,
430+
char *Constraints) {
431+
return InlineAsm::Verify(unwrap<FunctionType>(Ty), Constraints);
432+
}
433+
429434
extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm) {
430435
unwrap(M)->appendModuleInlineAsm(StringRef(Asm));
431436
}

Diff for: src/test/ui/inline-asm-bad-constraint.rs

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that the compiler will catch invalid inline assembly constraints.
12+
13+
#![feature(asm)]
14+
15+
extern "C" {
16+
fn foo(a: usize);
17+
}
18+
19+
fn main() {
20+
bad_register_constraint();
21+
bad_input();
22+
wrong_size_output();
23+
}
24+
25+
// Issue #54130
26+
fn bad_register_constraint() {
27+
let rax: u64;
28+
unsafe {
29+
asm!("" :"={rax"(rax)) //~ ERROR E0668
30+
};
31+
println!("Accumulator is: {}", rax);
32+
}
33+
34+
// Issue #54376
35+
fn bad_input() {
36+
unsafe {
37+
asm!("callq $0" : : "0"(foo)) //~ ERROR E0668
38+
};
39+
}
40+
41+
fn wrong_size_output() {
42+
let rax: u64 = 0;
43+
unsafe {
44+
asm!("addb $1, $0" : "={rax}"((0i32, rax))); //~ ERROR E0668
45+
}
46+
println!("rax: {}", rax);
47+
}

Diff for: src/test/ui/inline-asm-bad-constraint.stderr

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0668]: malformed inline assembly
2+
--> $DIR/inline-asm-bad-constraint.rs:29:9
3+
|
4+
LL | asm!("" :"={rax"(rax)) //~ ERROR E0668
5+
| ^^^^^^^^^^^^^^^^^^^^^^
6+
7+
error[E0668]: malformed inline assembly
8+
--> $DIR/inline-asm-bad-constraint.rs:37:9
9+
|
10+
LL | asm!("callq $0" : : "0"(foo)) //~ ERROR E0668
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error[E0668]: malformed inline assembly
14+
--> $DIR/inline-asm-bad-constraint.rs:44:9
15+
|
16+
LL | asm!("addb $1, $0" : "={rax}"((0i32, rax))); //~ ERROR E0668
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
19+
error: aborting due to 3 previous errors
20+
21+
For more information about this error, try `rustc --explain E0668`.

0 commit comments

Comments
 (0)