]>
Commit | Line | Data |
---|---|---|
98a07606 FG |
1 | From 33ab91103b3415e12457e3104f0e4517ce12d0f3 Mon Sep 17 00:00:00 2001 |
2 | From: Paolo Bonzini <pbonzini@redhat.com> | |
3 | Date: Thu, 12 Jan 2017 15:02:32 +0100 | |
4 | Subject: KVM: x86: fix emulation of "MOV SS, null selector" | |
5 | ||
6 | This is CVE-2017-2583. On Intel this causes a failed vmentry because | |
7 | SS's type is neither 3 nor 7 (even though the manual says this check is | |
8 | only done for usable SS, and the dmesg splat says that SS is unusable!). | |
9 | On AMD it's worse: svm.c is confused and sets CPL to 0 in the vmcb. | |
10 | ||
11 | The fix fabricates a data segment descriptor when SS is set to a null | |
12 | selector, so that CPL and SS.DPL are set correctly in the VMCS/vmcb. | |
13 | Furthermore, only allow setting SS to a NULL selector if SS.RPL < 3; | |
14 | this in turn ensures CPL < 3 because RPL must be equal to CPL. | |
15 | ||
16 | Thanks to Andy Lutomirski and Willy Tarreau for help in analyzing | |
17 | the bug and deciphering the manuals. | |
18 | ||
19 | Reported-by: Xiaohan Zhang <zhangxiaohan1@huawei.com> | |
20 | Fixes: 79d5b4c3cd809c770d4bf9812635647016c56011 | |
21 | Cc: stable@nongnu.org | |
22 | Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> | |
23 | --- | |
24 | arch/x86/kvm/emulate.c | 48 ++++++++++++++++++++++++++++++++++++++---------- | |
25 | 1 file changed, 38 insertions(+), 10 deletions(-) | |
26 | ||
27 | diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c | |
28 | index f36d0fa..cedbba0 100644 | |
29 | --- a/arch/x86/kvm/emulate.c | |
30 | +++ b/arch/x86/kvm/emulate.c | |
31 | @@ -1585,7 +1585,6 @@ static int write_segment_descriptor(struct x86_emulate_ctxt *ctxt, | |
32 | &ctxt->exception); | |
33 | } | |
34 | ||
35 | -/* Does not support long mode */ | |
36 | static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt, | |
37 | u16 selector, int seg, u8 cpl, | |
38 | enum x86_transfer_type transfer, | |
39 | @@ -1622,20 +1621,34 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt, | |
40 | ||
41 | rpl = selector & 3; | |
42 | ||
43 | - /* NULL selector is not valid for TR, CS and SS (except for long mode) */ | |
44 | - if ((seg == VCPU_SREG_CS | |
45 | - || (seg == VCPU_SREG_SS | |
46 | - && (ctxt->mode != X86EMUL_MODE_PROT64 || rpl != cpl)) | |
47 | - || seg == VCPU_SREG_TR) | |
48 | - && null_selector) | |
49 | - goto exception; | |
50 | - | |
51 | /* TR should be in GDT only */ | |
52 | if (seg == VCPU_SREG_TR && (selector & (1 << 2))) | |
53 | goto exception; | |
54 | ||
55 | - if (null_selector) /* for NULL selector skip all following checks */ | |
56 | + /* NULL selector is not valid for TR, CS and (except for long mode) SS */ | |
57 | + if (null_selector) { | |
58 | + if (seg == VCPU_SREG_CS || seg == VCPU_SREG_TR) | |
59 | + goto exception; | |
60 | + | |
61 | + if (seg == VCPU_SREG_SS) { | |
62 | + if (ctxt->mode != X86EMUL_MODE_PROT64 || rpl != cpl) | |
63 | + goto exception; | |
64 | + | |
65 | + /* | |
66 | + * ctxt->ops->set_segment expects the CPL to be in | |
67 | + * SS.DPL, so fake an expand-up 32-bit data segment. | |
68 | + */ | |
69 | + seg_desc.type = 3; | |
70 | + seg_desc.p = 1; | |
71 | + seg_desc.s = 1; | |
72 | + seg_desc.dpl = cpl; | |
73 | + seg_desc.d = 1; | |
74 | + seg_desc.g = 1; | |
75 | + } | |
76 | + | |
77 | + /* Skip all following checks */ | |
78 | goto load; | |
79 | + } | |
80 | ||
81 | ret = read_segment_descriptor(ctxt, selector, &seg_desc, &desc_addr); | |
82 | if (ret != X86EMUL_CONTINUE) | |
83 | @@ -1751,6 +1764,21 @@ static int load_segment_descriptor(struct x86_emulate_ctxt *ctxt, | |
84 | u16 selector, int seg) | |
85 | { | |
86 | u8 cpl = ctxt->ops->cpl(ctxt); | |
87 | + | |
88 | + /* | |
89 | + * None of MOV, POP and LSS can load a NULL selector in CPL=3, but | |
90 | + * they can load it at CPL<3 (Intel's manual says only LSS can, | |
91 | + * but it's wrong). | |
92 | + * | |
93 | + * However, the Intel manual says that putting IST=1/DPL=3 in | |
94 | + * an interrupt gate will result in SS=3 (the AMD manual instead | |
95 | + * says it doesn't), so allow SS=3 in __load_segment_descriptor | |
96 | + * and only forbid it here. | |
97 | + */ | |
98 | + if (seg == VCPU_SREG_SS && selector == 3 && | |
99 | + ctxt->mode == X86EMUL_MODE_PROT64) | |
100 | + return emulate_exception(ctxt, GP_VECTOR, 0, true); | |
101 | + | |
102 | return __load_segment_descriptor(ctxt, selector, seg, cpl, | |
103 | X86_TRANSFER_NONE, NULL); | |
104 | } | |
105 | -- | |
106 | cgit v0.12 | |
107 |