Skip to content

Commit e29c89f

Browse files
committed
kallsyms: Utilize ksymbol table for unauthorized address access
Access the system symbols with root permission to test whether it's possible to read and write the memory addresses of kernel-space from user-space. This helps in identifying potential vulnerabilities where user-space processes can inappropriately access kernel memory. Suggested-by: Rafael Aquini <[email protected]> Suggested-by: Cyril Hrubis <[email protected]> Signed-off-by: Li Wang <[email protected]> Reviewed-by: Cyril Hrubis <[email protected]>
1 parent 9725496 commit e29c89f

File tree

4 files changed

+154
-0
lines changed

4 files changed

+154
-0
lines changed

runtest/mm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ mmap10_2 mmap10 -s
5858
mmap10_3 mmap10 -a -s
5959
mmap10_4 mmap10 -a -s -i 60
6060

61+
kallsyms kallsyms
62+
6163
ksm01 ksm01
6264
ksm01_1 ksm01 -u 128
6365
ksm02 ksm02
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
kallsyms
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# SPDX-License-Identifier: GPL-2.0-or-later
2+
3+
top_srcdir ?= ../../../..
4+
5+
include $(top_srcdir)/include/mk/testcases.mk
6+
include $(top_srcdir)/include/mk/generic_leaf_target.mk
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Copyright (C) 2024 Red Hat, Inc.
4+
*/
5+
6+
/*\
7+
* [Description]
8+
*
9+
* Utilize kernel's symbol table for unauthorized address access.
10+
*
11+
* Access the system symbols with root permission to test whether it's
12+
* possible to read and write the memory addresses of kernel-space
13+
* from user-space. This helps in identifying potential vulnerabilities
14+
* where user-space processes can inappropriately access kernel memory.
15+
*
16+
* Steps:
17+
* 1. Start a process that reads all symbols and their addresses from
18+
* '/proc/kallsyms' and stores them in a linked list.
19+
*
20+
* 2. Attempt to write to each kernel address found in the linked list.
21+
* The expectation is that each attempt will fail with a SIGSEGV
22+
* (segmentation fault), indicating that the user-space process
23+
* cannot write to kernel memory.
24+
*
25+
* 3. Handle each SIGSEGV using a signal handler that sets a flag and
26+
* long jumps out of the faulting context.
27+
*
28+
* 4. If any write operation does not result in a SIGSEGV, log this as
29+
* a potential security vulnerability.
30+
*
31+
* 5. Observe and log the behavior and any system responses to these
32+
* unauthorized access attempts.
33+
*
34+
*/
35+
36+
#include <stdio.h>
37+
#include <stdlib.h>
38+
#include <assert.h>
39+
#include <unistd.h>
40+
#include <string.h>
41+
#include <setjmp.h>
42+
#include <signal.h>
43+
44+
#include "tst_test.h"
45+
#include "tst_safe_stdio.h"
46+
47+
struct kallsym {
48+
unsigned long addr;
49+
char type;
50+
char name[128];
51+
};
52+
53+
static struct kallsym *sym_table;
54+
static unsigned int nr_symbols;
55+
static sigjmp_buf jmpbuf;
56+
volatile sig_atomic_t segv_caught;
57+
58+
static void segv_handler(int sig)
59+
{
60+
if (sig == SIGSEGV)
61+
segv_caught++;
62+
else
63+
tst_res(TFAIL, "Unexpected signal %s", strsignal(sig));
64+
65+
siglongjmp(jmpbuf, 1);
66+
}
67+
68+
static unsigned int read_kallsyms(struct kallsym *table, unsigned int table_size)
69+
{
70+
char *line = NULL;
71+
size_t len = 0;
72+
unsigned int nr_syms = 0;
73+
FILE *stream = SAFE_FOPEN("/proc/kallsyms", "r");
74+
75+
while (getline(&line, &len, stream) != -1) {
76+
77+
if (table && nr_syms < table_size) {
78+
sscanf(line, "%lx %c %s",
79+
&table[nr_syms].addr,
80+
&table[nr_syms].type,
81+
table[nr_syms].name);
82+
}
83+
84+
nr_syms++;
85+
}
86+
87+
SAFE_FCLOSE(stream);
88+
89+
return nr_syms;
90+
}
91+
92+
static void setup(void)
93+
{
94+
struct sigaction sa;
95+
memset(&sa, 0, sizeof(sa));
96+
sa.sa_handler = segv_handler;
97+
sigaction(SIGSEGV, &sa, NULL);
98+
99+
nr_symbols = read_kallsyms(NULL, 0);
100+
sym_table = SAFE_CALLOC(nr_symbols, sizeof(*sym_table));
101+
unsigned int read_symbols = read_kallsyms(sym_table, nr_symbols);
102+
103+
if (nr_symbols != read_symbols)
104+
tst_res(TWARN, "/proc/kallsyms changed size!?");
105+
}
106+
107+
static void access_ksymbols_address(struct kallsym *table)
108+
{
109+
tst_res(TDEBUG, "Access kernel addr: 0x%lx (%c) (%s)",
110+
table->addr, table->type, table->name);
111+
112+
if (sigsetjmp(jmpbuf, 1) == 0) {
113+
*(volatile unsigned long *)table->addr = 0;
114+
115+
tst_res(TFAIL, "Successfully accessed kernel addr 0x%lx (%c) (%s)",
116+
table->addr, table->type, table->name);
117+
}
118+
}
119+
120+
static void test_access_kernel_address(void)
121+
{
122+
segv_caught = 0;
123+
124+
for (unsigned int i = 0; i < nr_symbols; i++)
125+
access_ksymbols_address(&sym_table[i]);
126+
127+
if (segv_caught == (sig_atomic_t)nr_symbols)
128+
tst_res(TPASS, "Caught %d SIGSEGV in access ksymbols addr", segv_caught);
129+
else
130+
tst_res(TFAIL, "Caught %d SIGSEGV but expected %d", segv_caught, nr_symbols);
131+
}
132+
133+
static void cleanup(void)
134+
{
135+
if (sym_table)
136+
free(sym_table);
137+
}
138+
139+
static struct tst_test test = {
140+
.needs_root = 1,
141+
.setup = setup,
142+
.cleanup = cleanup,
143+
.max_runtime = 60,
144+
.test_all = test_access_kernel_address,
145+
};

0 commit comments

Comments
 (0)