Commit-ID: ff9d78025c519046cfbc212b34f09116685402fc Gitweb: https://git.kernel.org/tip/ff9d78025c519046cfbc212b34f09116685402fc Author: Ricardo Neri <[email protected]> AuthorDate: Fri, 27 Oct 2017 13:25:34 -0700 Committer: Thomas Gleixner <[email protected]> CommitDate: Wed, 1 Nov 2017 21:50:09 +0100
x86/mpx: Do not use SIB.index if its value is 100b and ModRM.mod is not 11b Section 2.2.1.2 of the Intel 64 and IA-32 Architectures Software Developer's Manual volume 2A states that when ModRM.mod !=11b and ModRM.rm = 100b indexed register-indirect addressing is used. In other words, a SIB byte follows the ModRM byte. In the specific case of SIB.index = 100b, the scale*index portion of the computation of the effective address is null. To signal callers of this particular situation, get_reg_offset() can return -EDOM (-EINVAL continues to indicate that an error when decoding the SIB byte). An example of this situation can be the following instruction: 8b 4c 23 80 mov -0x80(%rbx,%riz,1),%rcx ModRM: 0x4c [mod:1b][reg:1b][rm:100b] SIB: 0x23 [scale:0b][index:100b][base:11b] Displacement: 0x80 (1-byte, as per ModRM.mod = 1b) The %riz 'register' indicates a null index. In long mode, a REX prefix may be used. When a REX prefix is present, REX.X adds a fourth bit to the register selection of SIB.index. This gives the ability to refer to all the 16 general purpose registers. When REX.X is 1b and SIB.index is 100b, the index is indicated in %r12. In our example, this would look like: 42 8b 4c 23 80 mov -0x80(%rbx,%r12,1),%rcx REX: 0x42 [W:0b][R:0b][X:1b][B:0b] ModRM: 0x4c [mod:1b][reg:1b][rm:100b] SIB: 0x23 [scale:0b][.X: 1b, index:100b][.B:0b, base:11b] Displacement: 0x80 (1-byte, as per ModRM.mod = 1b) %r12 is a valid register to use in the scale*index part of the effective address computation. Signed-off-by: Ricardo Neri <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Reviewed-by: Borislav Petkov <[email protected]> Cc: Adan Hawthorn <[email protected]> Cc: "Michael S. Tsirkin" <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Dave Hansen <[email protected]> Cc: [email protected] Cc: Paul Gortmaker <[email protected]> Cc: Huang Rui <[email protected]> Cc: Qiaowei Ren <[email protected]> Cc: Shuah Khan <[email protected]> Cc: Jonathan Corbet <[email protected]> Cc: Jiri Slaby <[email protected]> Cc: Nathan Howard <[email protected]> Cc: "Ravi V. Shankar" <[email protected]> Cc: Chris Metcalf <[email protected]> Cc: Brian Gerst <[email protected]> Cc: Andy Lutomirski <[email protected]> Cc: Colin Ian King <[email protected]> Cc: Chen Yucong <[email protected]> Cc: Adam Buchbinder <[email protected]> Cc: Vlastimil Babka <[email protected]> Cc: Lorenzo Stoakes <[email protected]> Cc: Masami Hiramatsu <[email protected]> Cc: Joe Perches <[email protected]> Cc: Paolo Bonzini <[email protected]> Cc: Andrew Morton <[email protected]> Link: https://lkml.kernel.org/r/1509135945-13762-8-git-send-email-ricardo.neri-calde...@linux.intel.com --- arch/x86/mm/mpx.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/arch/x86/mm/mpx.c b/arch/x86/mm/mpx.c index 57e5bf5..2ad1d4a 100644 --- a/arch/x86/mm/mpx.c +++ b/arch/x86/mm/mpx.c @@ -110,6 +110,15 @@ static int get_reg_offset(struct insn *insn, struct pt_regs *regs, regno = X86_SIB_INDEX(insn->sib.value); if (X86_REX_X(insn->rex_prefix.value)) regno += 8; + + /* + * If ModRM.mod != 3 and SIB.index = 4 the scale*index + * portion of the address computation is null. This is + * true only if REX.X is 0. In such a case, the SIB index + * is used in the address computation. + */ + if (X86_MODRM_MOD(insn->modrm.value) != 3 && regno == 4) + return -EDOM; break; case REG_TYPE_BASE: @@ -160,11 +169,19 @@ static void __user *mpx_get_addr_ref(struct insn *insn, struct pt_regs *regs) goto out; indx_offset = get_reg_offset(insn, regs, REG_TYPE_INDEX); - if (indx_offset < 0) + /* + * A negative offset generally means a error, except + * -EDOM, which means that the contents of the register + * should not be used as index. + */ + if (indx_offset == -EDOM) + indx = 0; + else if (indx_offset < 0) goto out; + else + indx = regs_get_register(regs, indx_offset); base = regs_get_register(regs, base_offset); - indx = regs_get_register(regs, indx_offset); eff_addr = base + indx * (1 << X86_SIB_SCALE(sib)); } else {

