@@ -22,9 +22,11 @@ int insn_get_modrm_reg_off(struct insn *insn, struct pt_regs *regs);
unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx);
int insn_get_code_seg_params(struct pt_regs *regs);
int insn_fetch_from_user(struct pt_regs *regs,
- unsigned char buf[MAX_INSN_SIZE]);
+ unsigned char buf[MAX_INSN_SIZE],
+ int *copied);
int insn_fetch_from_user_inatomic(struct pt_regs *regs,
- unsigned char buf[MAX_INSN_SIZE]);
+ unsigned char buf[MAX_INSN_SIZE],
+ int *copied);
bool insn_decode_from_regs(struct insn *insn, struct pt_regs *regs,
unsigned char buf[MAX_INSN_SIZE], int buf_size);
@@ -258,17 +258,17 @@ static int vc_fetch_insn_kernel(struct es_em_ctxt *ctxt,
static enum es_result __vc_decode_user_insn(struct es_em_ctxt *ctxt)
{
char buffer[MAX_INSN_SIZE];
- int res;
+ int insn_bytes = 0, res;
- res = insn_fetch_from_user_inatomic(ctxt->regs, buffer);
- if (!res) {
+ res = insn_fetch_from_user_inatomic(ctxt->regs, buffer, &insn_bytes);
+ if (res) {
ctxt->fi.vector = X86_TRAP_PF;
ctxt->fi.error_code = X86_PF_INSTR | X86_PF_USER;
ctxt->fi.cr2 = ctxt->regs->ip;
return ES_EXCEPTION;
}
- if (!insn_decode_from_regs(&ctxt->insn, ctxt->regs, buffer, res))
+ if (!insn_decode_from_regs(&ctxt->insn, ctxt->regs, buffer, insn_bytes))
return ES_DECODE_FAILED;
if (ctxt->insn.immediate.got)
@@ -346,13 +346,13 @@ bool fixup_umip_exception(struct pt_regs *regs)
if (!regs)
return false;
- nr_copied = insn_fetch_from_user(regs, buf);
-
/*
- * The insn_fetch_from_user above could have failed if user code
- * is protected by a memory protection key. Give up on emulation
- * in such a case. Should we issue a page fault?
+ * Give up on emulation if fetching the instruction failed. Should we
+ * issue a page fault or a #GP?
*/
+ if (!insn_fetch_from_user(regs, buf, NULL))
+ return false;
+
if (!nr_copied)
return false;
@@ -1442,27 +1442,36 @@ static int insn_get_effective_ip(struct pt_regs *regs, unsigned long *ip)
* insn_fetch_from_user() - Copy instruction bytes from user-space memory
* @regs: Structure with register values as seen when entering kernel mode
* @buf: Array to store the fetched instruction
+ * @copied: Pointer to an int where the number of copied instruction bytes
+ * is stored. Can be NULL.
*
* Gets the linear address of the instruction and copies the instruction bytes
* to the buf.
*
* Returns:
*
- * Number of instruction bytes copied.
+ * -EINVAL if the linear address of the instruction could not be calculated
+ * -EFAULT if nothing was copied
+ * 0 on success
*
- * 0 if nothing was copied.
*/
-int insn_fetch_from_user(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE])
+int insn_fetch_from_user(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE],
+ int *copied)
{
unsigned long ip;
int not_copied;
+ int bytes;
if (insn_get_effective_ip(regs, &ip))
- return 0;
+ return -EINVAL;
not_copied = copy_from_user(buf, (void __user *)ip, MAX_INSN_SIZE);
- return MAX_INSN_SIZE - not_copied;
+ bytes = MAX_INSN_SIZE - not_copied;
+ if (copied)
+ *copied = bytes;
+
+ return bytes ? 0 : -EFAULT;
}
/**
@@ -1470,27 +1479,36 @@ int insn_fetch_from_user(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE])
* while in atomic code
* @regs: Structure with register values as seen when entering kernel mode
* @buf: Array to store the fetched instruction
+ * @copied: Pointer to an int where the number of copied instruction bytes
+ * is stored. Can be NULL.
*
* Gets the linear address of the instruction and copies the instruction bytes
* to the buf. This function must be used in atomic context.
*
* Returns:
*
- * Number of instruction bytes copied.
+ * -EINVAL if the linear address of the instruction could not be calculated
+ * -EFAULT if nothing was copied
+ * 0 on success
*
- * 0 if nothing was copied.
*/
-int insn_fetch_from_user_inatomic(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE])
+int insn_fetch_from_user_inatomic(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE],
+ int *copied)
{
unsigned long ip;
int not_copied;
+ int bytes;
if (insn_get_effective_ip(regs, &ip))
- return 0;
+ return -EINVAL;
not_copied = __copy_from_user_inatomic(buf, (void __user *)ip, MAX_INSN_SIZE);
- return MAX_INSN_SIZE - not_copied;
+ bytes = MAX_INSN_SIZE - not_copied;
+ if (copied)
+ *copied = bytes;
+
+ return bytes ? 0 : -EFAULT;
}
/**