/* * If a bounds overflow occurs then a #BR is generated. This * function decodes MPX instructions to get violation address * and set this address into extended struct siginfo. * * Note that this is not a super precise way of doing this. * Userspace could have, by the time we get here, written * anything it wants in to the instructions. We can not * trust anything about it. They might not be valid * instructions or might encode invalid registers, etc... */ int mpx_fault_info(struct mpx_fault_info *info, struct pt_regs *regs) { const struct mpx_bndreg_state *bndregs; const struct mpx_bndreg *bndreg; struct insn insn; uint8_t bndregno; int err; err = mpx_insn_decode(&insn, regs); if (err) goto err_out; /* * We know at this point that we are only dealing with * MPX instructions. */ insn_get_modrm(&insn); bndregno = X86_MODRM_REG(insn.modrm.value); if (bndregno > 3) { err = -EINVAL; goto err_out; } /* get bndregs field from current task's xsave area */ bndregs = get_xsave_field_ptr(XFEATURE_MASK_BNDREGS); if (!bndregs) { err = -EINVAL; goto err_out; } /* now go select the individual register in the set of 4 */ bndreg = &bndregs->bndreg[bndregno]; /* * The registers are always 64-bit, but the upper 32 * bits are ignored in 32-bit mode. Also, note that the * upper bounds are architecturally represented in 1's * complement form. * * The 'unsigned long' cast is because the compiler * complains when casting from integers to different-size * pointers. */ info->lower = (void __user *)(unsigned long)bndreg->lower_bound; info->upper = (void __user *)(unsigned long)~bndreg->upper_bound; info->addr = insn_get_addr_ref(&insn, regs); /* * We were not able to extract an address from the instruction, * probably because there was something invalid in it. */ if (info->addr == (void __user *)-1) { err = -EINVAL; goto err_out; } trace_mpx_bounds_register_exception(info->addr, bndreg); return 0; err_out: /* info might be NULL, but kfree() handles that */ return err; }
/* * If a bounds overflow occurs then a #BR is generated. This * function decodes MPX instructions to get violation address * and set this address into extended struct siginfo. * * Note that this is not a super precise way of doing this. * Userspace could have, by the time we get here, written * anything it wants in to the instructions. We can not * trust anything about it. They might not be valid * instructions or might encode invalid registers, etc... * * The caller is expected to kfree() the returned siginfo_t. */ siginfo_t *mpx_generate_siginfo(struct pt_regs *regs) { const struct bndreg *bndregs, *bndreg; siginfo_t *info = NULL; struct insn insn; uint8_t bndregno; int err; err = mpx_insn_decode(&insn, regs); if (err) goto err_out; /* * We know at this point that we are only dealing with * MPX instructions. */ insn_get_modrm(&insn); bndregno = X86_MODRM_REG(insn.modrm.value); if (bndregno > 3) { err = -EINVAL; goto err_out; } /* get bndregs field from current task's xsave area */ bndregs = get_xsave_field_ptr(XSTATE_BNDREGS); if (!bndregs) { err = -EINVAL; goto err_out; } /* now go select the individual register in the set of 4 */ bndreg = &bndregs[bndregno]; info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { err = -ENOMEM; goto err_out; } /* * The registers are always 64-bit, but the upper 32 * bits are ignored in 32-bit mode. Also, note that the * upper bounds are architecturally represented in 1's * complement form. * * The 'unsigned long' cast is because the compiler * complains when casting from integers to different-size * pointers. */ info->si_lower = (void __user *)(unsigned long)bndreg->lower_bound; info->si_upper = (void __user *)(unsigned long)~bndreg->upper_bound; info->si_addr_lsb = 0; info->si_signo = SIGSEGV; info->si_errno = 0; info->si_code = SEGV_BNDERR; info->si_addr = mpx_get_addr_ref(&insn, regs); /* * We were not able to extract an address from the instruction, * probably because there was something invalid in it. */ if (info->si_addr == (void *)-1) { err = -EINVAL; goto err_out; } trace_mpx_bounds_register_exception(info->si_addr, bndreg); return info; err_out: /* info might be NULL, but kfree() handles that */ kfree(info); return ERR_PTR(err); }