-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathsysfs_devicetree_uefi_mmap.c
More file actions
148 lines (134 loc) · 5.48 KB
/
Copy pathsysfs_devicetree_uefi_mmap.c
File metadata and controls
148 lines (134 loc) · 5.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// This file is part of KASLD - https://github.com/bcoles/kasld
//
// Read the physical address of the EFI memory map buffer from the device
// tree sysfs chosen node. On UEFI-booted device tree platforms (ARM64,
// RISC-V, ARM32, PowerPC), the EFI stub records the physical address of
// the EFI memory map buffer into the FDT chosen node before handing off
// to the kernel:
//
// /sys/firmware/devicetree/base/chosen/linux,uefi-mmap-start
//
// This 8-byte big-endian property contains the physical address of the
// EFI memory map buffer — a DRAM allocation made by the EFI stub via
// EFI_BOOT_SERVICES.GetMemoryMap(). The buffer is in DRAM (not MMIO),
// so the address serves as a physical DRAM witness for phys_base bounding.
//
// This is distinct from the EFI runtime memory map (sysfs_efi_runtime_map.c,
// which reads /sys/firmware/efi/runtime-map/) — that sysfs reflects only
// entries with EFI_MEMORY_RUNTIME set, while this property holds the full
// pre-ExitBootServices() memory map buffer address.
//
// All device tree sysfs files are world-readable (mode 0444, set by
// drivers/of/kobj.c). No dmesg_restrict or sysctl gate applies.
//
// Leak primitive:
// Data leaked: physical address of EFI memory map buffer (DRAM)
// Kernel subsystem: drivers/firmware/efi/libstub/fdt.c — the EFI stub
// writes linux,uefi-mmap-start into the FDT chosen node
// (fdtparams.c only reads it back)
// Data structure: FDT chosen node (linux,uefi-mmap-start, u64 BE)
// Address type: physical (DRAM)
// Method: parsed (binary sysfs property)
// Status: unfixed (information exposure by design)
// Access check: none (world-readable sysfs attribute, 0444)
// Source:
// https://elixir.bootlin.com/linux/v6.12/source/drivers/firmware/efi/fdtparams.c
//
// Mitigations:
// CONFIG_OF=n removes device tree sysfs. CONFIG_EFI=n prevents the
// property from being written to the FDT. Non-EFI boots (U-Boot direct
// kernel entry without UEFI) will not have the property. The property
// is world-readable (0444); no runtime sysctl can restrict access.
// On decoupled architectures (ARM64, RISC-V 64), physical addresses
// cannot derive the virtual text base.
//
// Requires:
// - CONFIG_OF (device tree support)
// - CONFIG_EFI (UEFI firmware support)
// - UEFI-booted device tree platform (ARM64, ARM32, RISC-V, PowerPC)
//
// References:
// https://elixir.bootlin.com/linux/v6.12/source/drivers/firmware/efi/fdtparams.c
// https://elixir.bootlin.com/linux/v6.12/source/drivers/of/kobj.c#L65
// ---
// <bcoles@gmail.com>
#include "include/kasld/api.h"
#include "include/kasld/cli.h"
#include "include/kasld/devicetree.h"
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
KASLD_EXPLAIN(
"Reads the physical address of the EFI memory map buffer from the "
"device tree sysfs chosen node (/sys/firmware/devicetree/base/chosen/"
"linux,uefi-mmap-start). This world-readable 8-byte big-endian property "
"contains the physical DRAM address where the EFI stub allocated the "
"memory map buffer before ExitBootServices(). Only present on "
"UEFI-booted device tree platforms (ARM64, RISC-V, ARM32, PowerPC).");
KASLD_META("method:parsed\n"
"phase:inference\n"
"discloses:physical\n"
"config:CONFIG_OF\n"
"config:CONFIG_EFI\n");
/* Read a big-endian 64-bit value from raw bytes. */
static uint64_t read_be64(const unsigned char *p) {
uint64_t hi = kasld_dt_be32(p);
uint64_t lo = kasld_dt_be32(p + 4);
return (hi << 32) | lo;
}
int main(void) {
const char *bases[] = {"/sys/firmware/devicetree/base/chosen",
"/proc/device-tree/chosen", NULL};
const char *chosen = NULL;
char path[512];
unsigned char buf[8];
int n;
/* Probe: look for the property in either DT sysfs location */
/* A base that exists but is hidden is a different fact from one that is
* absent; keep it so the verdict below can say which. */
int dt_denied = 0;
for (int i = 0; bases[i]; i++) {
snprintf(path, sizeof(path), "%s/linux,uefi-mmap-start", bases[i]);
FILE *f = kasld_fopen(path, "rb");
if (f) {
fclose(f);
chosen = bases[i];
break;
}
if (errno == EACCES || errno == EPERM)
dt_denied = 1;
}
if (!chosen) {
kasld_err("device tree chosen node not found or no linux,uefi-mmap-start "
"property");
return dt_denied ? KASLD_EXIT_NOPERM : KASLD_EXIT_UNAVAILABLE;
}
kasld_info("trying %s/linux,uefi-mmap-start ...", chosen);
/* Read linux,uefi-mmap-start — always 8 bytes (u64 BE) */
snprintf(path, sizeof(path), "%s/linux,uefi-mmap-start", chosen);
n = kasld_dt_read_blob(path, buf, sizeof(buf));
if (n != 8) {
kasld_err("failed to read %s (got %d bytes, expected 8)", path, n);
return 0;
}
uint64_t mmap_phys = read_be64(buf);
if (!mmap_phys) {
kasld_err("linux,uefi-mmap-start is zero");
return 0;
}
/* Optionally read size for display context */
uint32_t mmap_size = 0;
snprintf(path, sizeof(path), "%s/linux,uefi-mmap-size", chosen);
n = kasld_dt_read_blob(path, buf, 4);
if (n == 4)
mmap_size = kasld_dt_be32(buf);
kasld_info("EFI memmap physical address: 0x%016llx",
(unsigned long long)mmap_phys);
if (mmap_size)
kasld_info("EFI memmap size: %u bytes", mmap_size);
kasld_result_sample(KASLD_TYPE_PHYS, REGION_EFI_MEMMAP,
(unsigned long)mmap_phys, NULL, CONF_PARSED);
return 0;
}