| From 7e46dddd6f6cd5dbf3c7bd04a7e75d19475ac9f2 Mon Sep 17 00:00:00 2001 |
| From: Nadav Amit <namit@cs.technion.ac.il> |
| Date: Tue, 28 Oct 2014 00:03:43 +0200 |
| Subject: KVM: x86: Fix far-jump to non-canonical check |
| |
| From: Nadav Amit <namit@cs.technion.ac.il> |
| |
| commit 7e46dddd6f6cd5dbf3c7bd04a7e75d19475ac9f2 upstream. |
| |
| Commit d1442d85cc30 ("KVM: x86: Handle errors when RIP is set during far |
| jumps") introduced a bug that caused the fix to be incomplete. Due to |
| incorrect evaluation, far jump to segment with L bit cleared (i.e., 32-bit |
| segment) and RIP with any of the high bits set (i.e, RIP[63:32] != 0) set may |
| not trigger #GP. As we know, this imposes a security problem. |
| |
| In addition, the condition for two warnings was incorrect. |
| |
| Fixes: d1442d85cc30ea75f7d399474ca738e0bc96f715 |
| Reported-by: Dan Carpenter <dan.carpenter@oracle.com> |
| Signed-off-by: Nadav Amit <namit@cs.technion.ac.il> |
| [Add #ifdef CONFIG_X86_64 to avoid complaints of undefined behavior. - Paolo] |
| Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/x86/kvm/emulate.c | 8 +++++--- |
| 1 file changed, 5 insertions(+), 3 deletions(-) |
| |
| --- a/arch/x86/kvm/emulate.c |
| +++ b/arch/x86/kvm/emulate.c |
| @@ -573,12 +573,14 @@ static inline int assign_eip_far(struct |
| case 4: |
| ctxt->_eip = (u32)dst; |
| break; |
| +#ifdef CONFIG_X86_64 |
| case 8: |
| if ((cs_l && is_noncanonical_address(dst)) || |
| - (!cs_l && (dst & ~(u32)-1))) |
| + (!cs_l && (dst >> 32) != 0)) |
| return emulate_gp(ctxt, 0); |
| ctxt->_eip = dst; |
| break; |
| +#endif |
| default: |
| WARN(1, "unsupported eip assignment size\n"); |
| } |
| @@ -2026,7 +2028,7 @@ static int em_jmp_far(struct x86_emulate |
| |
| rc = assign_eip_far(ctxt, ctxt->src.val, new_desc.l); |
| if (rc != X86EMUL_CONTINUE) { |
| - WARN_ON(!ctxt->mode != X86EMUL_MODE_PROT64); |
| + WARN_ON(ctxt->mode != X86EMUL_MODE_PROT64); |
| /* assigning eip failed; restore the old cs */ |
| ops->set_segment(ctxt, old_sel, &old_desc, 0, VCPU_SREG_CS); |
| return rc; |
| @@ -2123,7 +2125,7 @@ static int em_ret_far(struct x86_emulate |
| return rc; |
| rc = assign_eip_far(ctxt, eip, new_desc.l); |
| if (rc != X86EMUL_CONTINUE) { |
| - WARN_ON(!ctxt->mode != X86EMUL_MODE_PROT64); |
| + WARN_ON(ctxt->mode != X86EMUL_MODE_PROT64); |
| ops->set_segment(ctxt, old_cs, &old_desc, 0, VCPU_SREG_CS); |
| } |
| return rc; |