-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathdmesg_cma_reserved.c
More file actions
185 lines (160 loc) · 6.31 KB
/
Copy pathdmesg_cma_reserved.c
File metadata and controls
185 lines (160 loc) · 6.31 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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// This file is part of KASLD - https://github.com/bcoles/kasld
//
// CMA/DMA reserved memory pool physical addresses from dmesg.
//
// The kernel prints physical addresses when creating CMA, DMA, and
// restricted DMA memory pools, and when finalizing CMA reservations:
//
// Reserved memory: created CMA memory pool at 0x000000007a000000, size 96 MiB
// Reserved memory: created DMA memory pool at 0x0000000070000000, size 32 MiB
// Reserved memory: created restricted DMA pool at 0x0000000060000000, size 64
// MiB cma: Reserved 256 MiB at 0x00000000f0000000 on node -1
//
// These are common on ARM/ARM64/embedded and on systems with DMA-constrained
// devices. Less common on x86 desktop but present on many x86 servers.
//
// Leak primitive:
// Data leaked: physical addresses of CMA/DMA memory pool reservations
// Kernel subsystem: kernel/dma, mm/cma — reserved memory initialization
// Data structure: CMA/DMA pool base address (physical)
// Address type: physical (DRAM)
// Method: parsed (dmesg string)
// Status: unfixed (boot messages printed unconditionally)
// Access check: do_syslog() → check_syslog_permissions(); gated by
// dmesg_restrict
// Source:
// https://elixir.bootlin.com/linux/v6.6/source/kernel/dma/contiguous.c
//
// Mitigations:
// Access gated by dmesg_restrict (see dmesg.h for shared access gate
// details). The messages are printed unconditionally during boot when
// CMA/DMA pools are configured. On decoupled architectures, physical
// addresses cannot derive the virtual text base.
//
// Requires:
// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities; or
// readable /var/log/dmesg.
//
// References:
// https://elixir.bootlin.com/linux/v6.6/source/kernel/dma/contiguous.c
// https://elixir.bootlin.com/linux/v6.6/source/kernel/dma/coherent.c
// https://elixir.bootlin.com/linux/v6.6/source/kernel/dma/swiotlb.c
// https://elixir.bootlin.com/linux/v6.6/source/mm/cma.c
// ---
// <bcoles@gmail.com>
#define _GNU_SOURCE
#include "include/dmesg.h"
#include "include/kasld/api.h"
#include "include/kasld/cli.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define range_ctx addr_range
KASLD_EXPLAIN("Searches dmesg for CMA (Contiguous Memory Allocator) or DMA "
"reserved memory messages that print physical address ranges. "
"These boot-time messages reveal where the kernel reserved "
"contiguous physical memory for DMA operations. Access is gated "
"by dmesg_restrict.");
KASLD_META("method:parsed\n"
"phase:inference\n"
"discloses:physical\n"
"sysctl:dmesg_restrict>=1\n"
"bypass:CAP_SYSLOG\n"
"fallback:/var/log/dmesg\n");
static void update_range(struct range_ctx *r, unsigned long addr) {
if (!addr)
return;
if (!r->lo || addr < r->lo)
r->lo = addr;
if (addr > r->hi)
r->hi = addr;
}
/* Parse a "<num> MiB" size token at `s` to bytes. These kernel messages always
* print the pool size in MiB (contiguous.c / cma.c hardcode the unit); return 0
* (size unknown) for any other unit or on overflow, so the caller falls back to
* a base-only sample rather than fabricating a wrong extent. */
static unsigned long parse_mib_bytes(const char *s) {
char *e;
unsigned long mib = strtoul(s, &e, 10);
if (e == s)
return 0;
while (*e == ' ')
e++;
if (strncmp(e, "MiB", 3) != 0)
return 0;
unsigned long bytes;
if (kasld_mul_ovf(mib, MB, &bytes))
return 0;
return bytes;
}
/* Each pool is one contiguous reservation [addr, addr + size - 1]; emit it as a
* bounded range when the size is known so the engine excludes the whole
* forbidden band, else a base-only sample. Pools are sparse — the gaps between
* them are NOT known-empty — so range, never a covering extent. */
static void emit_pool(struct range_ctx *r, unsigned long addr,
unsigned long bytes) {
if (!addr)
return;
update_range(r, addr);
unsigned long end;
if (bytes && !kasld_add_ovf(addr, bytes - 1, &end))
kasld_result_range(KASLD_TYPE_PHYS, REGION_RESERVED_MEM, addr, end, NULL,
CONF_PARSED);
else
kasld_result_sample(KASLD_TYPE_PHYS, REGION_RESERVED_MEM, addr, NULL,
CONF_PARSED);
}
/* "Reserved memory: created CMA memory pool at 0x..., size N MiB"
* "Reserved memory: created DMA memory pool at 0x..., size N MiB"
* "Reserved memory: created restricted DMA pool at 0x..., size N MiB" */
static int on_reserved_pool(const char *line, void *ctx) {
struct range_ctx *r = ctx;
const char *p = strstr(line, " at ");
if (!p)
return 1;
unsigned long addr;
if (!kasld_addr_parse(p + 4, 16, &addr, NULL) || !addr)
return 1;
const char *s = strstr(p, ", size ");
unsigned long bytes = s ? parse_mib_bytes(s + 7) : 0;
kasld_info("Reserved memory pool at 0x%016lx", addr);
emit_pool(r, addr, bytes);
return 1; /* continue — may be multiple pools */
}
/* "cma: Reserved N MiB at 0x..."
* v5.x: "cma: Reserved 64 MiB at 0x00000000b4000000"
* v6.x+: "cma: Reserved 256 MiB at 0x00000000f0000000 on node -1" */
static int on_cma_reserved(const char *line, void *ctx) {
struct range_ctx *r = ctx;
const char *p = strstr(line, " at ");
if (!p)
return 1;
unsigned long addr;
if (!kasld_addr_parse(p + 4, 16, &addr, NULL) || !addr)
return 1;
const char *sz = strstr(line, "Reserved ");
unsigned long bytes = sz ? parse_mib_bytes(sz + 9) : 0;
kasld_info("CMA reservation at 0x%016lx", addr);
emit_pool(r, addr, bytes);
return 1; /* continue — may be multiple reservations */
}
int main(void) {
struct range_ctx r = {0, 0, 0};
kasld_info("searching dmesg for CMA/DMA reserved memory pools ...");
int ds = dmesg_search("Reserved memory: created", on_reserved_pool, &r);
if (ds < 0)
return KASLD_EXIT_NOPERM;
dmesg_search("cma: Reserved", on_cma_reserved, &r);
if (!r.lo) {
kasld_err("No CMA/DMA reserved memory pools found in dmesg");
return 0;
}
/* CMA pools are firmware/kernel-reserved memory carved out of DRAM —
* each is emitted as its own RESERVED_MEM band in the parse callbacks. */
kasld_info("lowest reserved pool: 0x%016lx", r.lo);
if (r.hi && r.hi != r.lo)
kasld_info("highest reserved pool: 0x%016lx", r.hi);
return 0;
}