Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/kern/aslr.c
288949 views
1
/*
2
* Copyright (c) 2026 The FreeBSD Foundation
3
*
4
* This software was developed by Mark Johnston under sponsorship from the
5
* FreeBSD Foundation.
6
*/
7
8
#include <sys/param.h>
9
#include <sys/procctl.h>
10
#include <sys/stat.h>
11
#include <sys/user.h>
12
#include <sys/wait.h>
13
14
#include <libutil.h>
15
#include <pwd.h>
16
#include <signal.h>
17
#include <stdlib.h>
18
#include <string.h>
19
#include <unistd.h>
20
21
#include <atf-c.h>
22
23
/*
24
* Spawn an unprivileged child with ASLR force-disabled, which then execs
25
* /sbin/ping (setuid root).
26
*/
27
static pid_t
28
spawn_ping(const atf_tc_t *tc)
29
{
30
const char *user;
31
struct passwd *passwd;
32
pid_t child;
33
int arg, error;
34
35
user = atf_tc_get_config_var(tc, "unprivileged_user");
36
passwd = getpwnam(user);
37
ATF_REQUIRE(passwd != NULL);
38
39
child = fork();
40
ATF_REQUIRE(child >= 0);
41
if (child == 0) {
42
if (seteuid(passwd->pw_uid) != 0)
43
_exit(1);
44
45
arg = PROC_ASLR_FORCE_DISABLE;
46
error = procctl(P_PID, getpid(), PROC_ASLR_CTL, &arg);
47
if (error != 0)
48
_exit(2);
49
50
execl("/sbin/ping", "ping", "127.0.0.1", NULL);
51
_exit(127);
52
}
53
usleep(500000); /* XXX-MJ */
54
55
return (child);
56
}
57
58
/*
59
* Return the base address of the first mapping backed by the specified
60
* executable in the given process, or 0 if not found.
61
*/
62
static uint64_t
63
text_base(pid_t pid, const char *path)
64
{
65
struct kinfo_vmentry *vmmap;
66
uint64_t base;
67
int cnt;
68
69
base = 0;
70
vmmap = kinfo_getvmmap(pid, &cnt);
71
if (vmmap == NULL)
72
return (0);
73
for (int i = 0; i < cnt; i++) {
74
if (vmmap[i].kve_type == KVME_TYPE_VNODE &&
75
strcmp(vmmap[i].kve_path, path) == 0) {
76
base = vmmap[i].kve_start;
77
break;
78
}
79
}
80
free(vmmap);
81
return (base);
82
}
83
84
/*
85
* Make sure that ASLR can't be disabled for a setuid executable by an
86
* unprivileged user.
87
*/
88
ATF_TC(aslr_setuid);
89
ATF_TC_HEAD(aslr_setuid, tc)
90
{
91
atf_tc_set_md_var(tc, "require.user", "root");
92
atf_tc_set_md_var(tc, "require.config", "unprivileged_user");
93
}
94
ATF_TC_BODY(aslr_setuid, tc)
95
{
96
struct stat sb;
97
uint64_t bases[5];
98
pid_t child, pid;
99
int arg, error, st;
100
101
if (!atf_tc_has_config_var(tc, "unprivileged_user"))
102
atf_tc_skip("unprivileged_user not set");
103
104
error = stat("/sbin/ping", &sb);
105
ATF_REQUIRE(error == 0);
106
ATF_REQUIRE_MSG(sb.st_uid == 0 && (sb.st_mode & S_ISUID) != 0,
107
"/sbin/ping is not setuid root");
108
109
child = spawn_ping(tc);
110
bases[0] = text_base(child, "/sbin/ping");
111
ATF_REQUIRE_MSG(bases[0] != 0,
112
"failed to find /sbin/ping text segment");
113
114
arg = 0;
115
error = procctl(P_PID, child, PROC_ASLR_STATUS, &arg);
116
ATF_REQUIRE_MSG(error == 0, "procctl ASLR_STATUS failed: %s",
117
strerror(errno));
118
ATF_REQUIRE_MSG((arg & PROC_ASLR_ACTIVE) != 0,
119
"ASLR is not active for setuid child");
120
ATF_REQUIRE_MSG((arg & ~PROC_ASLR_ACTIVE) == PROC_ASLR_NOFORCE,
121
"expected NOFORCE for setuid child, got %d",
122
arg & ~PROC_ASLR_ACTIVE);
123
124
error = kill(child, SIGTERM);
125
ATF_REQUIRE(error == 0);
126
pid = waitpid(child, &st, 0);
127
ATF_REQUIRE(pid == child);
128
ATF_REQUIRE(WIFSIGNALED(st) && WTERMSIG(st) == SIGTERM);
129
130
for (size_t i = 1; i < nitems(bases); i++) {
131
child = spawn_ping(tc);
132
bases[i] = text_base(child, "/sbin/ping");
133
ATF_REQUIRE_MSG(bases[i] != 0,
134
"failed to find /sbin/ping text segment");
135
error = kill(child, SIGTERM);
136
ATF_REQUIRE(error == 0);
137
pid = waitpid(child, &st, 0);
138
ATF_REQUIRE(pid == child);
139
ATF_REQUIRE(WIFSIGNALED(st) && WTERMSIG(st) == SIGTERM);
140
}
141
142
/* Verify that the text base is different across all runs. */
143
for (size_t i = 0; i < nitems(bases); i++) {
144
for (size_t j = i + 1; j < nitems(bases); j++) {
145
ATF_REQUIRE_MSG(bases[i] != bases[j],
146
"ping text base collision 0x%jx",
147
(uintmax_t)bases[i]);
148
}
149
}
150
}
151
152
ATF_TP_ADD_TCS(tp)
153
{
154
ATF_TP_ADD_TC(tp, aslr_setuid);
155
156
return (atf_no_error());
157
}
158
159