insn_get_addr_ref returns the effective address as defined by the section 3.7.5.1 Vol 1 of the Intel 64 and IA-32 Architectures Software Developer's Manual. In order to truly give the linear address, we must add the effective address to the segment base as described by the segment descriptor.
In most cases, the base will be 0 if the USER_DS segment is used or if segmentation is not used. However, the base address is not necessarily zero if a user programs defines its own segments. This is possible by using a local descriptor table.
Cc: Dave Hansen dave.hansen@linux.intel.com Cc: Adam Buchbinder adam.buchbinder@gmail.com Cc: Colin Ian King colin.king@canonical.com Cc: Lorenzo Stoakes lstoakes@gmail.com Cc: Qiaowei Ren qiaowei.ren@intel.com Cc: Arnaldo Carvalho de Melo acme@redhat.com Cc: Masami Hiramatsu mhiramat@kernel.org Cc: Adrian Hunter adrian.hunter@intel.com Cc: Kees Cook keescook@chromium.org Cc: Thomas Garnier thgarnie@google.com Cc: Peter Zijlstra peterz@infradead.org Cc: Borislav Petkov bp@suse.de Cc: Dmitry Vyukov dvyukov@google.com Cc: Ravi V. Shankar ravi.v.shankar@intel.com Cc: x86@kernel.org Signed-off-by: Ricardo Neri ricardo.neri-calderon@linux.intel.com --- arch/x86/lib/insn-eval.c | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c index d6525c2..b3a2fe8 100644 --- a/arch/x86/lib/insn-eval.c +++ b/arch/x86/lib/insn-eval.c @@ -523,6 +523,7 @@ static void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs) if (addr_offset < 0) goto out_err; addr = regs_get_register(regs, addr_offset); + addr += insn_get_seg_base(regs, insn, addr_offset); } else { if (insn->sib.nbytes) { /* @@ -551,6 +552,7 @@ static void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs) indx = regs_get_register(regs, indx_offset);
addr = base + indx * (1 << X86_SIB_SCALE(sib)); + addr += insn_get_seg_base(regs, insn, base_offset); } else { unsigned char addr_bytes;
@@ -575,8 +577,10 @@ static void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs) } else { addr = regs_get_register(regs, addr_offset); } + addr += insn_get_seg_base(regs, insn, addr_offset); } addr += insn->displacement.value; + } return (void __user *)addr; out_err: