-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathdmesg_swiotlb.c
More file actions
163 lines (138 loc) · 5.04 KB
/
Copy pathdmesg_swiotlb.c
File metadata and controls
163 lines (138 loc) · 5.04 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
// This file is part of KASLD - https://github.com/bcoles/kasld
//
// SWIOTLB (Software I/O TLB) prints the physical address range of the
// DMA bounce buffer pool during initialization. This is a large contiguous
// allocation within usable DRAM.
//
// Modern format (Linux ~4.17+):
// software IO TLB: mapped [mem 0x00000000bbed0000-0x00000000bfed0000] (64MB)
//
// Older format (Linux <4.17):
// Placing software IO TLB between 0xb7ed0000 and 0xbfed0000
//
// SWIOTLB is initialized on systems with IOMMU, VMs, or large-memory
// systems where some devices cannot address all physical memory.
//
// Leak primitive:
// Data leaked: physical address of SWIOTLB bounce buffer pool
// Kernel subsystem: kernel/dma/swiotlb — swiotlb_init()
// Data structure: SWIOTLB buffer physical address range
// 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.1.1/source/kernel/dma/swiotlb.c
//
// Mitigations:
// Access gated by dmesg_restrict (see dmesg.h for shared access gate
// details). Only printed when SWIOTLB is initialized (common on VMs
// and systems with IOMMU). 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.1.1/source/kernel/dma/swiotlb.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 SWIOTLB (Software I/O TLB) initialization "
"messages that print the physical address of the bounce buffer "
"pool. SWIOTLB is initialized on VMs and systems with IOMMU where "
"some devices cannot address all physical memory. 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");
/* Modern format: "mapped [mem 0x<start>-0x<end>]" */
static int on_mapped(const char *line, void *ctx) {
struct range_ctx *r = ctx;
const char *p = strstr(line, "[mem ");
if (!p)
return 1;
/* A physical range can exceed a 32-bit build's word on a PAE/LPAE kernel.
* Refuse the line: this region is a reserved band, so dropping one narrows
* what is excluded rather than widening it, and the aggregate stays sound. */
const char *endptr;
unsigned long start;
if (!kasld_addr_parse(p + 5, 16, &start, &endptr) || !start || *endptr != '-')
return 1;
unsigned long end;
if (!kasld_addr_parse(endptr + 1, 16, &end, &endptr) || !end)
return 1;
if (!r->lo || start < r->lo)
r->lo = start;
if (end > r->hi)
r->hi = end;
return 0; /* stop after first match */
}
/* Older format: "Placing software IO TLB between 0x<start> and 0x<end>" */
static int on_placing(const char *line, void *ctx) {
struct range_ctx *r = ctx;
const char *p = strstr(line, "between ");
if (!p)
return 1;
const char *endptr;
unsigned long start;
if (!kasld_addr_parse(p + 8, 16, &start, &endptr) || !start)
return 1;
const char *q = strstr(endptr, "and ");
if (!q)
return 1;
unsigned long end;
if (!kasld_addr_parse(q + 4, 16, &end, &endptr) || !end)
return 1;
if (!r->lo || start < r->lo)
r->lo = start;
if (end > r->hi)
r->hi = end;
return 0; /* stop after first match */
}
int main(void) {
struct range_ctx r = {0, 0, 0};
kasld_info("searching dmesg for SWIOTLB bounce buffer info ...");
/* Try modern format first */
int ds = dmesg_search("software IO TLB: mapped", on_mapped, &r);
if (ds < 0)
return KASLD_EXIT_NOPERM;
/* Fall back to older format */
if (!r.lo)
dmesg_search("Placing software IO TLB between", on_placing, &r);
if (!r.lo) {
kasld_err("SWIOTLB not found in dmesg (may not be enabled)");
return 0;
}
kasld_info("SWIOTLB start: 0x%016lx", r.lo);
/* The SWIOTLB pool is a single contiguous reservation (the search stops at
* the first match), so emit [start, end] as one bounded range: the engine
* excludes the whole forbidden band (phys_reservation_exclude), which a pair
* of disconnected interior points cannot drive. Not a covering — this lone
* reservation says nothing about the surrounding RAM, so range, not extent.
*/
if (r.hi && r.hi > r.lo) {
kasld_info("SWIOTLB end: 0x%016lx", r.hi);
kasld_result_range(KASLD_TYPE_PHYS, REGION_SWIOTLB, r.lo, r.hi, NULL,
CONF_PARSED);
} else {
kasld_result_sample(KASLD_TYPE_PHYS, REGION_SWIOTLB, r.lo, NULL,
CONF_PARSED);
}
return 0;
}