Section 2.2.1.3 of the Intel 64 and IA-32 Architectures Software
Developer's Manual volume 2A states that when ModRM.mod is zero and
ModRM.rm is 101b, a 32-bit displacement follows the ModRM byte. This means
that none of the registers are used in the computation of the effective
address. A return value of -EDOM indicates callers that they should not
use the value of registers when computing the effective address for the
instruction.

In long mode, the effective address is given by the 32-bit displacement
plus the location of the next instruction. In protected mode, only the
displacement is used.

The instruction decoder takes care of obtaining the displacement.

Cc: Dave Hansen <[email protected]>
Cc: Adam Buchbinder <[email protected]>
Cc: Colin Ian King <[email protected]>
Cc: Lorenzo Stoakes <[email protected]>
Cc: Qiaowei Ren <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Adrian Hunter <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Thomas Garnier <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Borislav Petkov <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: Ravi V. Shankar <[email protected]>
Cc: [email protected]
Signed-off-by: Ricardo Neri <[email protected]>
---
 arch/x86/lib/insn-eval.c | 26 +++++++++++++++++++++++---
 1 file changed, 23 insertions(+), 3 deletions(-)

diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c
index a8e12bd0aecd..04f696c3793e 100644
--- a/arch/x86/lib/insn-eval.c
+++ b/arch/x86/lib/insn-eval.c
@@ -360,6 +360,14 @@ static int get_reg_offset(struct insn *insn, struct 
pt_regs *regs,
        switch (type) {
        case REG_TYPE_RM:
                regno = X86_MODRM_RM(insn->modrm.value);
+
+               /*
+                * ModRM.mod == 0 and ModRM.rm == 5 means a 32-bit displacement
+                * follows the ModRM byte.
+                */
+               if (!X86_MODRM_MOD(insn->modrm.value) && regno == 5)
+                       return -EDOM;
+
                if (X86_REX_B(insn->rex_prefix.value))
                        regno += 8;
                break;
@@ -706,10 +714,22 @@ void __user *insn_get_addr_ref(struct insn *insn, struct 
pt_regs *regs)
                        eff_addr = base + indx * (1 << X86_SIB_SCALE(sib));
                } else {
                        addr_offset = get_reg_offset(insn, regs, REG_TYPE_RM);
-                       if (addr_offset < 0)
-                               goto out_err;
 
-                       eff_addr = regs_get_register(regs, addr_offset);
+                       /*
+                        * -EDOM means that we must ignore the address_offset.
+                        * In such a case, in 64-bit mode the effective address
+                        * relative to the RIP of the following instruction.
+                        */
+                       if (addr_offset == -EDOM) {
+                               if (user_64bit_mode(regs))
+                                       eff_addr = (long)regs->ip + 
insn->length;
+                               else
+                                       eff_addr = 0;
+                       } else if (addr_offset < 0) {
+                               goto out_err;
+                       } else {
+                               eff_addr = regs_get_register(regs, addr_offset);
+                       }
                }
 
                eff_addr += insn->displacement.value;
-- 
2.13.0

Reply via email to