Loading...
Note: File does not exist in v6.2.
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Landlock tests - Signal Scoping
4 *
5 * Copyright © 2024 Tahera Fahimi <fahimitahera@gmail.com>
6 */
7
8#define _GNU_SOURCE
9#include <errno.h>
10#include <fcntl.h>
11#include <linux/landlock.h>
12#include <pthread.h>
13#include <signal.h>
14#include <sys/prctl.h>
15#include <sys/types.h>
16#include <sys/wait.h>
17#include <unistd.h>
18
19#include "common.h"
20#include "scoped_common.h"
21
22/* This variable is used for handling several signals. */
23static volatile sig_atomic_t is_signaled;
24
25/* clang-format off */
26FIXTURE(scoping_signals) {};
27/* clang-format on */
28
29FIXTURE_VARIANT(scoping_signals)
30{
31 int sig;
32};
33
34/* clang-format off */
35FIXTURE_VARIANT_ADD(scoping_signals, sigtrap) {
36 /* clang-format on */
37 .sig = SIGTRAP,
38};
39
40/* clang-format off */
41FIXTURE_VARIANT_ADD(scoping_signals, sigurg) {
42 /* clang-format on */
43 .sig = SIGURG,
44};
45
46/* clang-format off */
47FIXTURE_VARIANT_ADD(scoping_signals, sighup) {
48 /* clang-format on */
49 .sig = SIGHUP,
50};
51
52/* clang-format off */
53FIXTURE_VARIANT_ADD(scoping_signals, sigtstp) {
54 /* clang-format on */
55 .sig = SIGTSTP,
56};
57
58FIXTURE_SETUP(scoping_signals)
59{
60 drop_caps(_metadata);
61
62 is_signaled = 0;
63}
64
65FIXTURE_TEARDOWN(scoping_signals)
66{
67}
68
69static void scope_signal_handler(int sig, siginfo_t *info, void *ucontext)
70{
71 if (sig == SIGTRAP || sig == SIGURG || sig == SIGHUP || sig == SIGTSTP)
72 is_signaled = 1;
73}
74
75/*
76 * In this test, a child process sends a signal to parent before and
77 * after getting scoped.
78 */
79TEST_F(scoping_signals, send_sig_to_parent)
80{
81 int pipe_parent[2];
82 int status;
83 pid_t child;
84 pid_t parent = getpid();
85 struct sigaction action = {
86 .sa_sigaction = scope_signal_handler,
87 .sa_flags = SA_SIGINFO,
88
89 };
90
91 ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
92 ASSERT_LE(0, sigaction(variant->sig, &action, NULL));
93
94 /* The process should not have already been signaled. */
95 EXPECT_EQ(0, is_signaled);
96
97 child = fork();
98 ASSERT_LE(0, child);
99 if (child == 0) {
100 char buf_child;
101 int err;
102
103 EXPECT_EQ(0, close(pipe_parent[1]));
104
105 /*
106 * The child process can send signal to parent when
107 * domain is not scoped.
108 */
109 err = kill(parent, variant->sig);
110 ASSERT_EQ(0, err);
111 ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
112 EXPECT_EQ(0, close(pipe_parent[0]));
113
114 create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
115
116 /*
117 * The child process cannot send signal to the parent
118 * anymore.
119 */
120 err = kill(parent, variant->sig);
121 ASSERT_EQ(-1, err);
122 ASSERT_EQ(EPERM, errno);
123
124 /*
125 * No matter of the domain, a process should be able to
126 * send a signal to itself.
127 */
128 ASSERT_EQ(0, is_signaled);
129 ASSERT_EQ(0, raise(variant->sig));
130 ASSERT_EQ(1, is_signaled);
131
132 _exit(_metadata->exit_code);
133 return;
134 }
135 EXPECT_EQ(0, close(pipe_parent[0]));
136
137 /* Waits for a first signal to be received, without race condition. */
138 while (!is_signaled && !usleep(1))
139 ;
140 ASSERT_EQ(1, is_signaled);
141 ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
142 EXPECT_EQ(0, close(pipe_parent[1]));
143 is_signaled = 0;
144
145 ASSERT_EQ(child, waitpid(child, &status, 0));
146 if (WIFSIGNALED(status) || !WIFEXITED(status) ||
147 WEXITSTATUS(status) != EXIT_SUCCESS)
148 _metadata->exit_code = KSFT_FAIL;
149
150 EXPECT_EQ(0, is_signaled);
151}
152
153/* clang-format off */
154FIXTURE(scoped_domains) {};
155/* clang-format on */
156
157#include "scoped_base_variants.h"
158
159FIXTURE_SETUP(scoped_domains)
160{
161 drop_caps(_metadata);
162}
163
164FIXTURE_TEARDOWN(scoped_domains)
165{
166}
167
168/*
169 * This test ensures that a scoped process cannot send signal out of
170 * scoped domain.
171 */
172TEST_F(scoped_domains, check_access_signal)
173{
174 pid_t child;
175 pid_t parent = getpid();
176 int status;
177 bool can_signal_child, can_signal_parent;
178 int pipe_parent[2], pipe_child[2];
179 char buf_parent;
180 int err;
181
182 can_signal_parent = !variant->domain_child;
183 can_signal_child = !variant->domain_parent;
184
185 if (variant->domain_both)
186 create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
187
188 ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
189 ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC));
190
191 child = fork();
192 ASSERT_LE(0, child);
193 if (child == 0) {
194 char buf_child;
195
196 EXPECT_EQ(0, close(pipe_child[0]));
197 EXPECT_EQ(0, close(pipe_parent[1]));
198
199 if (variant->domain_child)
200 create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
201
202 ASSERT_EQ(1, write(pipe_child[1], ".", 1));
203 EXPECT_EQ(0, close(pipe_child[1]));
204
205 /* Waits for the parent to send signals. */
206 ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
207 EXPECT_EQ(0, close(pipe_parent[0]));
208
209 err = kill(parent, 0);
210 if (can_signal_parent) {
211 ASSERT_EQ(0, err);
212 } else {
213 ASSERT_EQ(-1, err);
214 ASSERT_EQ(EPERM, errno);
215 }
216 /*
217 * No matter of the domain, a process should be able to
218 * send a signal to itself.
219 */
220 ASSERT_EQ(0, raise(0));
221
222 _exit(_metadata->exit_code);
223 return;
224 }
225 EXPECT_EQ(0, close(pipe_parent[0]));
226 EXPECT_EQ(0, close(pipe_child[1]));
227
228 if (variant->domain_parent)
229 create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
230
231 ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1));
232 EXPECT_EQ(0, close(pipe_child[0]));
233
234 err = kill(child, 0);
235 if (can_signal_child) {
236 ASSERT_EQ(0, err);
237 } else {
238 ASSERT_EQ(-1, err);
239 ASSERT_EQ(EPERM, errno);
240 }
241 ASSERT_EQ(0, raise(0));
242
243 ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
244 EXPECT_EQ(0, close(pipe_parent[1]));
245 ASSERT_EQ(child, waitpid(child, &status, 0));
246
247 if (WIFSIGNALED(status) || !WIFEXITED(status) ||
248 WEXITSTATUS(status) != EXIT_SUCCESS)
249 _metadata->exit_code = KSFT_FAIL;
250}
251
252static int thread_pipe[2];
253
254enum thread_return {
255 THREAD_INVALID = 0,
256 THREAD_SUCCESS = 1,
257 THREAD_ERROR = 2,
258};
259
260void *thread_func(void *arg)
261{
262 char buf;
263
264 if (read(thread_pipe[0], &buf, 1) != 1)
265 return (void *)THREAD_ERROR;
266
267 return (void *)THREAD_SUCCESS;
268}
269
270TEST(signal_scoping_threads)
271{
272 pthread_t no_sandbox_thread, scoped_thread;
273 enum thread_return ret = THREAD_INVALID;
274
275 drop_caps(_metadata);
276 ASSERT_EQ(0, pipe2(thread_pipe, O_CLOEXEC));
277
278 ASSERT_EQ(0,
279 pthread_create(&no_sandbox_thread, NULL, thread_func, NULL));
280
281 /* Restricts the domain after creating the first thread. */
282 create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
283
284 ASSERT_EQ(EPERM, pthread_kill(no_sandbox_thread, 0));
285 ASSERT_EQ(1, write(thread_pipe[1], ".", 1));
286
287 ASSERT_EQ(0, pthread_create(&scoped_thread, NULL, thread_func, NULL));
288 ASSERT_EQ(0, pthread_kill(scoped_thread, 0));
289 ASSERT_EQ(1, write(thread_pipe[1], ".", 1));
290
291 EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret));
292 EXPECT_EQ(THREAD_SUCCESS, ret);
293 EXPECT_EQ(0, pthread_join(scoped_thread, (void **)&ret));
294 EXPECT_EQ(THREAD_SUCCESS, ret);
295
296 EXPECT_EQ(0, close(thread_pipe[0]));
297 EXPECT_EQ(0, close(thread_pipe[1]));
298}
299
300const short backlog = 10;
301
302static volatile sig_atomic_t signal_received;
303
304static void handle_sigurg(int sig)
305{
306 if (sig == SIGURG)
307 signal_received = 1;
308 else
309 signal_received = -1;
310}
311
312static int setup_signal_handler(int signal)
313{
314 struct sigaction sa = {
315 .sa_handler = handle_sigurg,
316 };
317
318 if (sigemptyset(&sa.sa_mask))
319 return -1;
320
321 sa.sa_flags = SA_SIGINFO | SA_RESTART;
322 return sigaction(SIGURG, &sa, NULL);
323}
324
325/* clang-format off */
326FIXTURE(fown) {};
327/* clang-format on */
328
329enum fown_sandbox {
330 SANDBOX_NONE,
331 SANDBOX_BEFORE_FORK,
332 SANDBOX_BEFORE_SETOWN,
333 SANDBOX_AFTER_SETOWN,
334};
335
336FIXTURE_VARIANT(fown)
337{
338 const enum fown_sandbox sandbox_setown;
339};
340
341/* clang-format off */
342FIXTURE_VARIANT_ADD(fown, no_sandbox) {
343 /* clang-format on */
344 .sandbox_setown = SANDBOX_NONE,
345};
346
347/* clang-format off */
348FIXTURE_VARIANT_ADD(fown, sandbox_before_fork) {
349 /* clang-format on */
350 .sandbox_setown = SANDBOX_BEFORE_FORK,
351};
352
353/* clang-format off */
354FIXTURE_VARIANT_ADD(fown, sandbox_before_setown) {
355 /* clang-format on */
356 .sandbox_setown = SANDBOX_BEFORE_SETOWN,
357};
358
359/* clang-format off */
360FIXTURE_VARIANT_ADD(fown, sandbox_after_setown) {
361 /* clang-format on */
362 .sandbox_setown = SANDBOX_AFTER_SETOWN,
363};
364
365FIXTURE_SETUP(fown)
366{
367 drop_caps(_metadata);
368}
369
370FIXTURE_TEARDOWN(fown)
371{
372}
373
374/*
375 * Sending an out of bound message will trigger the SIGURG signal
376 * through file_send_sigiotask.
377 */
378TEST_F(fown, sigurg_socket)
379{
380 int server_socket, recv_socket;
381 struct service_fixture server_address;
382 char buffer_parent;
383 int status;
384 int pipe_parent[2], pipe_child[2];
385 pid_t child;
386
387 memset(&server_address, 0, sizeof(server_address));
388 set_unix_address(&server_address, 0);
389
390 ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
391 ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC));
392
393 if (variant->sandbox_setown == SANDBOX_BEFORE_FORK)
394 create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
395
396 child = fork();
397 ASSERT_LE(0, child);
398 if (child == 0) {
399 int client_socket;
400 char buffer_child;
401
402 EXPECT_EQ(0, close(pipe_parent[1]));
403 EXPECT_EQ(0, close(pipe_child[0]));
404
405 ASSERT_EQ(0, setup_signal_handler(SIGURG));
406 client_socket = socket(AF_UNIX, SOCK_STREAM, 0);
407 ASSERT_LE(0, client_socket);
408
409 /* Waits for the parent to listen. */
410 ASSERT_EQ(1, read(pipe_parent[0], &buffer_child, 1));
411 ASSERT_EQ(0, connect(client_socket, &server_address.unix_addr,
412 server_address.unix_addr_len));
413
414 /*
415 * Waits for the parent to accept the connection, sandbox
416 * itself, and call fcntl(2).
417 */
418 ASSERT_EQ(1, read(pipe_parent[0], &buffer_child, 1));
419 /* May signal itself. */
420 ASSERT_EQ(1, send(client_socket, ".", 1, MSG_OOB));
421 EXPECT_EQ(0, close(client_socket));
422 ASSERT_EQ(1, write(pipe_child[1], ".", 1));
423 EXPECT_EQ(0, close(pipe_child[1]));
424
425 /* Waits for the message to be received. */
426 ASSERT_EQ(1, read(pipe_parent[0], &buffer_child, 1));
427 EXPECT_EQ(0, close(pipe_parent[0]));
428
429 if (variant->sandbox_setown == SANDBOX_BEFORE_SETOWN) {
430 ASSERT_EQ(0, signal_received);
431 } else {
432 /*
433 * A signal is only received if fcntl(F_SETOWN) was
434 * called before any sandboxing or if the signal
435 * receiver is in the same domain.
436 */
437 ASSERT_EQ(1, signal_received);
438 }
439 _exit(_metadata->exit_code);
440 return;
441 }
442 EXPECT_EQ(0, close(pipe_parent[0]));
443 EXPECT_EQ(0, close(pipe_child[1]));
444
445 server_socket = socket(AF_UNIX, SOCK_STREAM, 0);
446 ASSERT_LE(0, server_socket);
447 ASSERT_EQ(0, bind(server_socket, &server_address.unix_addr,
448 server_address.unix_addr_len));
449 ASSERT_EQ(0, listen(server_socket, backlog));
450 ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
451
452 recv_socket = accept(server_socket, NULL, NULL);
453 ASSERT_LE(0, recv_socket);
454
455 if (variant->sandbox_setown == SANDBOX_BEFORE_SETOWN)
456 create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
457
458 /*
459 * Sets the child to receive SIGURG for MSG_OOB. This uncommon use is
460 * a valid attack scenario which also simplifies this test.
461 */
462 ASSERT_EQ(0, fcntl(recv_socket, F_SETOWN, child));
463
464 if (variant->sandbox_setown == SANDBOX_AFTER_SETOWN)
465 create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
466
467 ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
468
469 /* Waits for the child to send MSG_OOB. */
470 ASSERT_EQ(1, read(pipe_child[0], &buffer_parent, 1));
471 EXPECT_EQ(0, close(pipe_child[0]));
472 ASSERT_EQ(1, recv(recv_socket, &buffer_parent, 1, MSG_OOB));
473 EXPECT_EQ(0, close(recv_socket));
474 EXPECT_EQ(0, close(server_socket));
475 ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
476 EXPECT_EQ(0, close(pipe_parent[1]));
477
478 ASSERT_EQ(child, waitpid(child, &status, 0));
479 if (WIFSIGNALED(status) || !WIFEXITED(status) ||
480 WEXITSTATUS(status) != EXIT_SUCCESS)
481 _metadata->exit_code = KSFT_FAIL;
482}
483
484TEST_HARNESS_MAIN