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 \<lkurusa@acm.org\>
This commit is contained in:
@@ -30,7 +30,7 @@ pub fn codegen_inline_asm(
|
|||||||
ia: &hir::InlineAsm,
|
ia: &hir::InlineAsm,
|
||||||
outputs: Vec<PlaceRef<'ll, 'tcx>>,
|
outputs: Vec<PlaceRef<'ll, 'tcx>>,
|
||||||
mut inputs: Vec<&'ll Value>
|
mut inputs: Vec<&'ll Value>
|
||||||
) {
|
) -> bool {
|
||||||
let mut ext_constraints = vec![];
|
let mut ext_constraints = vec![];
|
||||||
let mut output_types = vec![];
|
let mut output_types = vec![];
|
||||||
|
|
||||||
@@ -97,6 +97,10 @@ pub fn codegen_inline_asm(
|
|||||||
ia.alignstack,
|
ia.alignstack,
|
||||||
dialect
|
dialect
|
||||||
);
|
);
|
||||||
|
if r.is_none() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let r = r.unwrap();
|
||||||
|
|
||||||
// Again, based on how many outputs we have
|
// Again, based on how many outputs we have
|
||||||
let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect);
|
let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect);
|
||||||
@@ -117,6 +121,8 @@ pub fn codegen_inline_asm(
|
|||||||
llvm::LLVMSetMetadata(r, kind,
|
llvm::LLVMSetMetadata(r, kind,
|
||||||
llvm::LLVMMDNodeInContext(bx.cx.llcx, &val, 1));
|
llvm::LLVMMDNodeInContext(bx.cx.llcx, &val, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn codegen_global_asm<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
|
pub fn codegen_global_asm<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
|
||||||
|
|||||||
@@ -737,7 +737,7 @@ impl Builder<'a, 'll, 'tcx> {
|
|||||||
pub fn inline_asm_call(&self, asm: *const c_char, cons: *const c_char,
|
pub fn inline_asm_call(&self, asm: *const c_char, cons: *const c_char,
|
||||||
inputs: &[&'ll Value], output: &'ll Type,
|
inputs: &[&'ll Value], output: &'ll Type,
|
||||||
volatile: bool, alignstack: bool,
|
volatile: bool, alignstack: bool,
|
||||||
dia: AsmDialect) -> &'ll Value {
|
dia: AsmDialect) -> Option<&'ll Value> {
|
||||||
self.count_insn("inlineasm");
|
self.count_insn("inlineasm");
|
||||||
|
|
||||||
let volatile = if volatile { llvm::True }
|
let volatile = if volatile { llvm::True }
|
||||||
@@ -753,9 +753,17 @@ impl Builder<'a, 'll, 'tcx> {
|
|||||||
debug!("Asm Output Type: {:?}", output);
|
debug!("Asm Output Type: {:?}", output);
|
||||||
let fty = Type::func(&argtys[..], output);
|
let fty = Type::func(&argtys[..], output);
|
||||||
unsafe {
|
unsafe {
|
||||||
|
// Ask LLVM to verify that the constraints are well-formed.
|
||||||
|
let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons);
|
||||||
|
debug!("Constraint verification result: {:?}", constraints_ok);
|
||||||
|
if constraints_ok == llvm::True {
|
||||||
let v = llvm::LLVMRustInlineAsm(
|
let v = llvm::LLVMRustInlineAsm(
|
||||||
fty, asm, cons, volatile, alignstack, dia);
|
fty, asm, cons, volatile, alignstack, dia);
|
||||||
self.call(v, inputs, None)
|
Some(self.call(v, inputs, None))
|
||||||
|
} else {
|
||||||
|
// LLVM has detected an issue with our constaints, bail out
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,4 +47,26 @@ unsafe { simd_add(i32x2(0, 0), i32x2(1, 2)); } // ok!
|
|||||||
```
|
```
|
||||||
"##,
|
"##,
|
||||||
|
|
||||||
|
E0668: r##"
|
||||||
|
Malformed inline assembly rejected by LLVM.
|
||||||
|
|
||||||
|
LLVM checks the validity of the constraints and the assembly string passed to
|
||||||
|
it. This error implies that LLVM seems something wrong with the inline
|
||||||
|
assembly call.
|
||||||
|
|
||||||
|
In particular, it can happen if you forgot the closing bracket of a register
|
||||||
|
constraint (see issue #51430):
|
||||||
|
```ignore (error-emitted-at-codegen-which-cannot-be-handled-by-compile_fail)
|
||||||
|
#![feature(asm)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let rax: u64;
|
||||||
|
unsafe {
|
||||||
|
asm!("" :"={rax"(rax));
|
||||||
|
println!("Accumulator is: {}", rax);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
"##,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1208,6 +1208,9 @@ extern "C" {
|
|||||||
AlignStack: Bool,
|
AlignStack: Bool,
|
||||||
Dialect: AsmDialect)
|
Dialect: AsmDialect)
|
||||||
-> &Value;
|
-> &Value;
|
||||||
|
pub fn LLVMRustInlineAsmVerify(Ty: &Type,
|
||||||
|
Constraints: *const c_char)
|
||||||
|
-> Bool;
|
||||||
|
|
||||||
pub fn LLVMRustDebugMetadataVersion() -> u32;
|
pub fn LLVMRustDebugMetadataVersion() -> u32;
|
||||||
pub fn LLVMRustVersionMajor() -> u32;
|
pub fn LLVMRustVersionMajor() -> u32;
|
||||||
|
|||||||
@@ -86,7 +86,11 @@ impl FunctionCx<'a, 'll, 'tcx> {
|
|||||||
self.codegen_operand(&bx, input).immediate()
|
self.codegen_operand(&bx, input).immediate()
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
asm::codegen_inline_asm(&bx, asm, outputs, input_vals);
|
let res = asm::codegen_inline_asm(&bx, asm, outputs, input_vals);
|
||||||
|
if !res {
|
||||||
|
span_err!(bx.sess(), statement.source_info.span, E0668,
|
||||||
|
"malformed inline assembly");
|
||||||
|
}
|
||||||
bx
|
bx
|
||||||
}
|
}
|
||||||
mir::StatementKind::FakeRead(..) |
|
mir::StatementKind::FakeRead(..) |
|
||||||
|
|||||||
@@ -426,6 +426,11 @@ extern "C" LLVMValueRef LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString,
|
|||||||
HasSideEffects, IsAlignStack, fromRust(Dialect)));
|
HasSideEffects, IsAlignStack, fromRust(Dialect)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty,
|
||||||
|
char *Constraints) {
|
||||||
|
return InlineAsm::Verify(unwrap<FunctionType>(Ty), Constraints);
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm) {
|
extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm) {
|
||||||
unwrap(M)->appendModuleInlineAsm(StringRef(Asm));
|
unwrap(M)->appendModuleInlineAsm(StringRef(Asm));
|
||||||
}
|
}
|
||||||
|
|||||||
47
src/test/ui/inline-asm-bad-constraint.rs
Normal file
47
src/test/ui/inline-asm-bad-constraint.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Test that the compiler will catch invalid inline assembly constraints.
|
||||||
|
|
||||||
|
#![feature(asm)]
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn foo(a: usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
bad_register_constraint();
|
||||||
|
bad_input();
|
||||||
|
wrong_size_output();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue #54130
|
||||||
|
fn bad_register_constraint() {
|
||||||
|
let rax: u64;
|
||||||
|
unsafe {
|
||||||
|
asm!("" :"={rax"(rax)) //~ ERROR E0668
|
||||||
|
};
|
||||||
|
println!("Accumulator is: {}", rax);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue #54376
|
||||||
|
fn bad_input() {
|
||||||
|
unsafe {
|
||||||
|
asm!("callq $0" : : "0"(foo)) //~ ERROR E0668
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrong_size_output() {
|
||||||
|
let rax: u64 = 0;
|
||||||
|
unsafe {
|
||||||
|
asm!("addb $1, $0" : "={rax}"((0i32, rax))); //~ ERROR E0668
|
||||||
|
}
|
||||||
|
println!("rax: {}", rax);
|
||||||
|
}
|
||||||
21
src/test/ui/inline-asm-bad-constraint.stderr
Normal file
21
src/test/ui/inline-asm-bad-constraint.stderr
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
error[E0668]: malformed inline assembly
|
||||||
|
--> $DIR/inline-asm-bad-constraint.rs:29:9
|
||||||
|
|
|
||||||
|
LL | asm!("" :"={rax"(rax)) //~ ERROR E0668
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error[E0668]: malformed inline assembly
|
||||||
|
--> $DIR/inline-asm-bad-constraint.rs:37:9
|
||||||
|
|
|
||||||
|
LL | asm!("callq $0" : : "0"(foo)) //~ ERROR E0668
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error[E0668]: malformed inline assembly
|
||||||
|
--> $DIR/inline-asm-bad-constraint.rs:44:9
|
||||||
|
|
|
||||||
|
LL | asm!("addb $1, $0" : "={rax}"((0i32, rax))); //~ ERROR E0668
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0668`.
|
||||||
Reference in New Issue
Block a user