Skip to content

Commit 8288df7

Browse files
committed
sysfs_kernel_notes_xen: record CVE-2024-26816 and correct the config gating
Tag the /sys/kernel/notes KASLR leak as CVE-2024-26816 (a CVE: field + a cve: KASLD_META key), and key the config: gate on CONFIG_XEN_PV. The prerequisite is not merely CONFIG_XEN: startup_xen and hypercall_page need CONFIG_XEN_PV (the CVE's scope), while pvh_start_xen needs CONFIG_PVH -- which a KVM/QEMU PVH guest can enable without CONFIG_XEN. The fix was backported to stable (6.6.23, 6.1.83, 5.15.153, ...), so version alone does not decide affectedness; the runtime staleness check does. Document base recovery -- pvh_start_xen sits at/near _text, so the orchestrator floors the lowest interior sample to KASLR_VIRT_ALIGN to reach _text (on 6.x hypercall_page is far in .noinstr.text and startup_xen farther still) -- standardise the open-failure path to kasld_err, and drop an unused include.
1 parent 84a566c commit 8288df7

1 file changed

Lines changed: 53 additions & 35 deletions

File tree

src/components/sysfs_kernel_notes_xen.c

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@
33
// Parse ELF notes from /sys/kernel/notes for leaked kernel pointers on
44
// x86(_64) kernels.
55
//
6-
// Xen ELF notes on kernels with CONFIG_XEN=y contain KASLR-adjusted virtual
7-
// addresses generated via _ASM_PTR in arch/x86/xen/xen-head.S:
8-
// - Type 1 (XEN_ELFNOTE_ENTRY): startup_xen virtual address
9-
// - Type 2 (XEN_ELFNOTE_HYPERCALL_PAGE): hypercall_page virtual address
10-
// - Type 18 (XEN_ELFNOTE_PHYS32_ENTRY): virtual KASLR offset (pvh_start_xen -
11-
// __START_KERNEL_map)
6+
// Xen ELF notes embed KASLR-adjusted virtual addresses generated via _ASM_PTR
7+
// in the kernel image. The gating config differs per note:
8+
// - Type 1 (XEN_ELFNOTE_ENTRY): startup_xen VA [CONFIG_XEN_PV]
9+
// - Type 2 (XEN_ELFNOTE_HYPERCALL_PAGE): hypercall_page VA [CONFIG_XEN_PV]
10+
// - Type 18 (XEN_ELFNOTE_PHYS32_ENTRY): pvh_start_xen - [CONFIG_PVH]
11+
// __START_KERNEL_map (a virtual
12+
// offset)
13+
// So it is not merely CONFIG_XEN: the PV notes need CONFIG_XEN_PV and the PVH
14+
// note needs CONFIG_PVH (a KVM/QEMU PVH guest enables CONFIG_PVH without
15+
// CONFIG_XEN). CVE-2024-26816 is scoped to CONFIG_XEN_PV; on a distro kernel
16+
// CONFIG_XEN=y pulls in both. Sources: arch/x86/xen/xen-head.S,
17+
// arch/x86/platform/pvh/head.S.
1218
//
1319
// Also performs a generic scan of all remaining note descriptors for
1420
// pointer-sized values in the kernel text virtual address range.
@@ -32,29 +38,37 @@
3238
//
3339
// Leak primitive:
3440
// Data leaked: kernel text virtual address (startup_xen, hypercall_page)
41+
// CVE: CVE-2024-26816
3542
// Kernel subsystem: arch/x86/xen — /sys/kernel/notes (ELF notes)
3643
// Data structure: Xen ELF notes (XEN_ELFNOTE_ENTRY,
3744
// XEN_ELFNOTE_HYPERCALL_PAGE) Address type: virtual (kernel text) Method:
38-
// exact (ELF note parsing) Patched: v6.9 (commit aaa8736370db);
39-
// hardened v6.13 (223abe96ac0d) Status: fixed in v6.9
40-
// Access check: none (world-readable /sys/kernel/notes, 0444)
45+
// exact (ELF note parsing) Patched: v6.9 (CVE-2024-26816,
46+
// aaa8736370db); hardened v6.13 (223abe96ac0d) Status: fixed in
47+
// v6.9 Access check: none (world-readable /sys/kernel/notes, 0444)
4148
// Source:
4249
// https://elixir.bootlin.com/linux/v6.7.3/source/arch/x86/xen/xen-head.S#L118
4350
//
4451
// Mitigations:
4552
// Patched in v6.9 (relocations in .notes skipped). Further hardened in
46-
// v6.13 (place-relative relocations). Requires CONFIG_XEN=y.
47-
// /sys/kernel/notes is world-readable (0444); no runtime sysctl
48-
// can restrict access.
53+
// v6.13 (place-relative relocations). Requires CONFIG_XEN_PV and/or
54+
// CONFIG_PVH (per the notes above). /sys/kernel/notes is world-readable
55+
// (0444); no runtime sysctl can restrict access.
4956
//
5057
// Requires:
5158
// - Readable /sys/kernel/notes
52-
// - CONFIG_XEN=y (for Xen-specific notes; generic scan works without it)
59+
// - CONFIG_XEN_PV (startup_xen, hypercall_page) and/or CONFIG_PVH
60+
// (pvh_start_xen)
61+
// for the Xen notes; the generic note scan works without either
5362
//
54-
// Patched in v6.9-rc1~164^2~8 (aaa8736370db) — relocations in .notes section
55-
// are skipped, so values no longer reflect the KASLR-adjusted addresses.
56-
// Further hardened in v6.13-rc1~202^2~2 (223abe96ac0d) — Xen ELF notes use
57-
// place-relative relocations to prevent leaking the KASLR base.
63+
// Patched in v6.9-rc1~164^2~8 (aaa8736370db, CVE-2024-26816) — relocations in
64+
// the .notes section are skipped, so values no longer reflect the
65+
// KASLR-adjusted addresses (they become identical to System.map). Backported to
66+
// the 2024-03-27 stable batch
67+
// (6.6.23, 6.1.83, 5.15.153, 5.10.214, 5.4.273, 4.19.311, ...), so a build's
68+
// version does not determine whether it is affected; the staleness check below
69+
// distinguishes patched from vulnerable notes at runtime. Further hardened in
70+
// v6.13-rc1~202^2~2 (223abe96ac0d) — Xen ELF notes use place-relative
71+
// relocations to prevent leaking the KASLR base.
5872
//
5973
// References:
6074
// https://cateee.net/lkddb/web-lkddb/XEN.html
@@ -75,7 +89,6 @@
7589
#include <fcntl.h>
7690
#include <stdint.h>
7791
#include <stdio.h>
78-
#include <stdlib.h>
7992
#include <string.h>
8093
#include <sys/types.h>
8194
#include <unistd.h>
@@ -96,8 +109,9 @@ KASLD_EXPLAIN("On Xen PV and PVH guests, /sys/kernel/notes contains ELF notes "
96109
KASLD_META("method:parsed\n"
97110
"phase:inference\n"
98111
"addr:virtual\n"
112+
"cve:CVE-2024-26816\n"
99113
"patch:v6.9\n"
100-
"config:CONFIG_XEN\n");
114+
"config:CONFIG_XEN_PV\n");
101115

102116
/* Check if /proc/kallsyms contains xen_elfnote_* global symbols,
103117
* indicating v6.13+ place-relative encoding where Xen ELF note values
@@ -127,6 +141,18 @@ static int has_xen_elfnote_symbols(void) {
127141
return 0;
128142
}
129143

144+
/* The image base is recovered from these notes by alignment. On x86_64 _text is
145+
* 2 MiB-aligned (CONFIG_PHYSICAL_ALIGN, the KASLR step), and the PHYS32_ENTRY
146+
* note resolves to pvh_start_xen, which sits within a few KiB of _text (_text+0
147+
* .. _text+0x5f0 across 5.x/6.x). The orchestrator floors the lowest interior
148+
* sample emitted below to KASLR_VIRT_ALIGN, landing on _text.
149+
*
150+
* pvh_start_xen is the anchor for that floor. hypercall_page sits at _text +
151+
* 0x1000 on <= 5.x (inside the 2 MiB window), but 6.x moves it to .noinstr.text
152+
* ~16-19 MiB past _text, so its floor overshoots _text by ~16 MiB; startup_xen
153+
* (.init.text) is ~25-40 MiB out. All three are emitted as interior samples;
154+
* the engine bounds the base from the lowest. */
155+
130156
int main(void) {
131157
int fd;
132158
uint32_t hdr[3]; /* namesz, descsz, type */
@@ -143,7 +169,7 @@ int main(void) {
143169

144170
fd = kasld_open("/sys/kernel/notes", O_RDONLY);
145171
if (fd < 0) {
146-
perror("[-] open(/sys/kernel/notes)");
172+
kasld_err("/sys/kernel/notes unavailable");
147173
return (errno == EACCES || errno == EPERM) ? KASLD_EXIT_NOPERM
148174
: KASLD_EXIT_UNAVAILABLE;
149175
}
@@ -286,12 +312,13 @@ int main(void) {
286312
}
287313
if (xen_phys32) {
288314
/* PHYS32_ENTRY stores pvh_start_xen - __START_KERNEL_map, not a
289-
* hardware physical address. On x86_64, the kernel text is mapped
290-
* at __START_KERNEL_map + virt_offset, so this value IS the virtual
291-
* KASLR offset. Adding __START_KERNEL_map recovers the virtual
292-
* address of pvh_start_xen, which sits at or very near _stext.
293-
* The hardware physical load address is independently randomized
294-
* and is not recoverable from this note. */
315+
* hardware physical address. On x86_64 the kernel text is mapped at
316+
* __START_KERNEL_map + virt_offset, so this value IS the virtual KASLR
317+
* offset; adding __START_KERNEL_map recovers pvh_start_xen's virtual
318+
* address, which sits at or very near _text — the sample the
319+
* orchestrator floors to KASLR_VIRT_ALIGN to recover _text (see the
320+
* note above main()). The hardware physical load address is
321+
* independently randomized and is not recoverable from this note. */
295322
unsigned long virt = KERNEL_VIRT_TEXT_MIN + xen_phys32;
296323
if (kasld_addr_is_kernel_text(virt)) {
297324
kasld_found("Xen PHYS32_ENTRY -> virtual: %lx", virt);
@@ -308,14 +335,5 @@ int main(void) {
308335
if (!found)
309336
kasld_err("no kernel addresses found in ELF notes");
310337

311-
// NOTE: On kernels <= 5.x, hypercall_page was in .pushsection .text
312-
// (at _text + 0x1000), so addr & -KASLR_VIRT_ALIGN recovered _text exactly.
313-
// In 6.x, hypercall_page moved to .pushsection .noinstr.text for
314-
// instrumentation isolation. The linker places .noinstr.text after the
315-
// bulk of kernel code, putting hypercall_page millions of bytes past
316-
// _text. addr & -KASLR_VIRT_ALIGN then overshoots by ~16+ MiB.
317-
// The orchestrator handles this correctly since it does not assume a
318-
// fixed symbol-to-base offset.
319-
320338
return 0;
321339
}

0 commit comments

Comments
 (0)