From 0a0c3ad3910036ac22cdfc496b832b42e7a9ea67 Mon Sep 17 00:00:00 2001 From: Matt Borgerson Date: Tue, 10 Sep 2024 15:31:58 -0700 Subject: [PATCH] amd64: Fix 32b cmpxchg unmodified register writeback 32b register writes are zero-extended. In case a register should not be modified by cmpxchg, preserve the entire 64b value. Discovered via QEMU differential tests. --- priv/guest_amd64_toIR.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/priv/guest_amd64_toIR.c b/priv/guest_amd64_toIR.c index 4df9aaa5f..8bb7f72a1 100644 --- a/priv/guest_amd64_toIR.c +++ b/priv/guest_amd64_toIR.c @@ -8677,10 +8677,25 @@ ULong dis_cmpxchg_G_E ( /*OUT*/Bool* ok, assign( acc, getIRegRAX(size) ); setFlags_DEP1_DEP2(Iop_Sub8, acc, dest, ty); assign( cond, mk_amd64g_calculate_condition(AMD64CondZ) ); - assign( dest2, IRExpr_ITE(mkexpr(cond), mkexpr(src), mkexpr(dest)) ); - assign( acc2, IRExpr_ITE(mkexpr(cond), mkexpr(acc), mkexpr(dest)) ); - putIRegRAX(size, mkexpr(acc2)); - putIRegE(size, pfx, rm, mkexpr(dest2)); + + if (size == 4) { + /* Preserve upper 32b when register should be unmodified. */ + IRTemp acc64 = newTemp(Ity_I64); + IRTemp dest64 = newTemp(Ity_I64); + assign( dest64, IRExpr_ITE(mkexpr(cond), + unop(Iop_32Uto64, mkexpr(src)), + getIRegE(8, pfx, rm)) ); + assign( acc64, IRExpr_ITE(mkexpr(cond), + getIRegRAX(8), + unop(Iop_32Uto64, mkexpr(dest))) ); + putIRegRAX(8, mkexpr(acc64)); + putIRegE(8, pfx, rm, mkexpr(dest64)); + } else { + assign( dest2, IRExpr_ITE(mkexpr(cond), mkexpr(src), mkexpr(dest)) ); + assign( acc2, IRExpr_ITE(mkexpr(cond), mkexpr(acc), mkexpr(dest)) ); + putIRegRAX(size, mkexpr(acc2)); + putIRegE(size, pfx, rm, mkexpr(dest2)); + } DIP("cmpxchg%c %s,%s\n", nameISize(size), nameIRegG(size,pfx,rm), nameIRegE(size,pfx,rm) );