1412: fix(singlepass-backend) Fix argument overwriting in a corner case when calling functions. r=nlewycky a=losfair

Fixes #1409.

Co-authored-by: losfair <zhy20000919@hotmail.com>
Co-authored-by: Heyang Zhou <zhy20000919@hotmail.com>
This commit is contained in:
bors[bot] 2020-04-29 16:34:06 +00:00 committed by GitHub
commit 0cba64b538
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 64 additions and 5 deletions

View File

@ -1942,7 +1942,10 @@ impl X64FunctionCode {
/// Emits a System V call sequence.
///
/// This function must not use RAX before `cb` is called.
/// This function will not use RAX before `cb` is called.
///
/// The caller MUST NOT hold any temporary registers allocated by `acquire_temp_gpr` when calling
/// this function.
fn emit_call_sysv<I: Iterator<Item = Location>, F: FnOnce(&mut Assembler)>(
a: &mut Assembler,
m: &mut Machine,
@ -2063,14 +2066,17 @@ impl X64FunctionCode {
// Dummy value slot to be filled with `mov`.
a.emit_push(Size::S64, Location::GPR(GPR::RAX));
// Use R10 as the temporary register here, since it is callee-saved and not
// used by the callback `cb`.
a.emit_mov(Size::S64, *param, Location::GPR(GPR::R10));
// Use RCX as the temporary register here, since:
// - It is a temporary register that is not used for any persistent value.
// - This register as an argument location is only written to after `sort_call_movs`.
m.reserve_unused_temp_gpr(GPR::RCX);
a.emit_mov(Size::S64, *param, Location::GPR(GPR::RCX));
a.emit_mov(
Size::S64,
Location::GPR(GPR::R10),
Location::GPR(GPR::RCX),
Location::Memory(GPR::RSP, 0),
);
m.release_temp_gpr(GPR::RCX);
}
Location::XMM(_) => {
// Dummy value slot to be filled with `mov`.

View File

@ -0,0 +1,53 @@
;; A bug was introduced in the commit below, where the `R10` register is incorrectly overwritten.
;; This test case covers this specific case.
;;
;; https://github.com/wasmerio/wasmer/commit/ed826cb389b17273002e729160bf076213b7e2f2#diff-8c30560d501545a19acafa7ebb21ebfeR1784
;;
(module
(func $call_target (param i64) (param i64) (param i64) (param i64) (param i64) (param i64) (result i64)
(local.get 0)
)
(func (export "test") (result i64)
;; Use `i64.add`s to actually push values onto the runtime stack.
;; rsi
(i64.const 1)
(i64.const 1)
(i64.add)
;; rdi
(i64.const 1)
(i64.const 1)
(i64.add)
;; r8
(i64.const 1)
(i64.const 1)
(i64.add)
;; r9
(i64.const 1)
(i64.const 1)
(i64.add)
;; r10 (!)
(i64.const 1)
(i64.const 1)
(i64.add)
;; Imm64's as arguments
(i64.const 1)
(i64.const 1)
(i64.const 1)
(i64.const 1)
(i64.const 1) ;; allocated to the first memory slot
;; call
(call $call_target)
(return)
)
)
(assert_return (invoke "test") (i64.const 2))