diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index ea188a338afbc1db2097081ddefa345e82c79730..ca82ec9c5ffe6693663003c9773baaf969823d1a 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -708,7 +708,7 @@ static int segmented_read_std(struct x86_emulate_ctxt *ctxt, * Prefetch the remaining bytes of the instruction without crossing page * boundary if they are not in fetch_cache yet. */ -static int do_insn_fetch_bytes(struct x86_emulate_ctxt *ctxt) +static int do_insn_fetch_bytes(struct x86_emulate_ctxt *ctxt, int op_size) { struct fetch_cache *fc = &ctxt->fetch; int rc; @@ -720,7 +720,14 @@ static int do_insn_fetch_bytes(struct x86_emulate_ctxt *ctxt) cur_size = fc->end - fc->start; size = min(15UL - cur_size, PAGE_SIZE - offset_in_page(fc->end)); - if (unlikely(size == 0)) + + /* + * One instruction can only straddle two pages, + * and one has been loaded at the beginning of + * x86_decode_insn. So, if not enough bytes + * still, we must have hit the 15-byte boundary. + */ + if (unlikely(size < op_size)) return X86EMUL_UNHANDLEABLE; rc = __linearize(ctxt, addr, size, false, true, &linear); if (unlikely(rc != X86EMUL_CONTINUE)) @@ -736,17 +743,18 @@ static int do_insn_fetch_bytes(struct x86_emulate_ctxt *ctxt) static int do_insn_fetch(struct x86_emulate_ctxt *ctxt, void *__dest, unsigned size) { - int rc; struct fetch_cache *fc = &ctxt->fetch; u8 *dest = __dest; u8 *src = &fc->data[ctxt->_eip - fc->start]; + /* We have to be careful about overflow! */ + if (unlikely(ctxt->_eip > fc->end - size)) { + int rc = do_insn_fetch_bytes(ctxt, size); + if (rc != X86EMUL_CONTINUE) + return rc; + } + while (size--) { - if (unlikely(ctxt->_eip == fc->end)) { - rc = do_insn_fetch_bytes(ctxt); - if (rc != X86EMUL_CONTINUE) - return rc; - } *dest++ = *src++; ctxt->_eip++; continue; @@ -4228,7 +4236,7 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len) if (insn_len > 0) memcpy(ctxt->fetch.data, insn, insn_len); else { - rc = do_insn_fetch_bytes(ctxt); + rc = do_insn_fetch_bytes(ctxt, 1); if (rc != X86EMUL_CONTINUE) return rc; }