-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathdmesg_reserved_mem.c
More file actions
130 lines (112 loc) · 4.42 KB
/
Copy pathdmesg_reserved_mem.c
File metadata and controls
130 lines (112 loc) · 4.42 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
// This file is part of KASLD - https://github.com/bcoles/kasld
//
// The device tree reserved memory subsystem prints physical address ranges
// of all reserved memory regions to dmesg during boot:
//
// OF: reserved mem: 0x0000000080000000..0x00000000801fffff (2048 KiB)
// nomap non-reusable mmode_resv0@80000000
// OF: reserved mem: 0x0000000088000000..0x000000008bffffff (65536 KiB)
// map reusable linux,cma@88000000
//
// These messages are emitted unconditionally (pr_info) during early FDT
// reserved memory initialization. Present on ARM, ARM64, RISC-V, MIPS,
// PowerPC, and any architecture using device trees.
//
// This is a generic version covering all reserved-mem nodes. The existing
// dmesg_reserved_mem_opensbi component specifically targets OpenSBI
// mmode_resv0 entries on RISC-V.
//
// Leak primitive:
// Data leaked: physical address ranges of device tree reserved memory
// Kernel subsystem: drivers/of/of_reserved_mem — __reserved_mem_init_node()
// Data structure: reserved memory node entries (physical address + size)
// Address type: physical (DRAM)
// Method: parsed (dmesg string)
// Status: unfixed (printed unconditionally during boot)
// Access check: do_syslog() → check_syslog_permissions(); gated by
// dmesg_restrict
// Source:
// https://elixir.bootlin.com/linux/v6.12/source/drivers/of/of_reserved_mem.c#L463
//
// Mitigations:
// Access gated by dmesg_restrict (see dmesg.h for shared access gate
// details). Only present on device tree platforms (ARM, ARM64, RISC-V,
// MIPS, PPC). 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.12/source/drivers/of/of_reserved_mem.c#L463
// ---
// <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 device tree reserved memory messages (OF: "
"reserved mem) that print physical address ranges for firmware-"
"reserved regions. Common on ARM, ARM64, RISC-V, MIPS, and "
"PowerPC. 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");
/* Parse "0x<start>..0x<end>" from reserved mem lines */
static int on_match(const char *line, void *ctx) {
struct range_ctx *r = ctx;
const char *p = strstr(line, "0x");
if (!p)
return 1;
const char *endptr;
unsigned long start;
if (!kasld_addr_parse(p, 16, &start, &endptr) || !start || endptr[0] != '.' ||
endptr[1] != '.')
return 1;
/* skip ".." */
const char *q = strstr(endptr, "0x");
if (!q)
return 1;
unsigned long end;
if (!kasld_addr_parse(q, 16, &end, &endptr) || !end)
return 1;
if (!r->lo || start < r->lo)
r->lo = start;
if (end > r->hi)
r->hi = end;
/* Each "OF: reserved mem:" line is one contiguous reserved region fully
* spanning [start, end] (end is the inclusive last address), so emit it as a
* bounded range: the engine can then exclude the whole forbidden band
* (phys_reservation_exclude), which a pair of disconnected interior points
* cannot drive. Reserved regions are sparse — the gaps between them are NOT
* known-empty — so this is a range, never a covering extent. */
if (end > start)
kasld_result_range(KASLD_TYPE_PHYS, REGION_RESERVED_MEM, start, end, NULL,
CONF_PARSED);
return 1; /* continue — multiple regions */
}
int main(void) {
struct range_ctx r = {0, 0, 0};
kasld_info("searching dmesg for device tree reserved memory regions ...");
int ds = dmesg_search("OF: reserved mem:", on_match, &r);
if (!r.lo) {
kasld_err("no device tree reserved memory regions found in dmesg");
if (ds < 0)
return KASLD_EXIT_NOPERM;
return 0;
}
kasld_info("lowest reserved mem physical address: 0x%016lx", r.lo);
kasld_info("highest reserved mem physical address: 0x%016lx", r.hi);
/* Per-region forbidden bands are emitted in on_match(). */
return 0;
}