File Index
Files and directories
Sign in
FILE

scoped_signal_test.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/landlock/scoped_signal_test.c C
  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
252enum thread_return {
253	THREAD_INVALID = 0,
254	THREAD_SUCCESS = 1,
255	THREAD_ERROR = 2,
256	THREAD_TEST_FAILED = 3,
257};
258
259static void *thread_sync(void *arg)
260{
261	const int pipe_read = *(int *)arg;
262	char buf;
263
264	if (read(pipe_read, &buf, 1) != 1)
265		return (void *)THREAD_ERROR;
266
267	return (void *)THREAD_SUCCESS;
268}
269
270TEST(signal_scoping_thread_before)
271{
272	pthread_t no_sandbox_thread;
273	enum thread_return ret = THREAD_INVALID;
274	int thread_pipe[2];
275
276	drop_caps(_metadata);
277	ASSERT_EQ(0, pipe2(thread_pipe, O_CLOEXEC));
278
279	ASSERT_EQ(0, pthread_create(&no_sandbox_thread, NULL, thread_sync,
280				    &thread_pipe[0]));
281
282	/* Enforces restriction after creating the thread. */
283	create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
284
285	EXPECT_EQ(0, pthread_kill(no_sandbox_thread, 0));
286	EXPECT_EQ(1, write(thread_pipe[1], ".", 1));
287
288	EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret));
289	EXPECT_EQ(THREAD_SUCCESS, ret);
290
291	EXPECT_EQ(0, close(thread_pipe[0]));
292	EXPECT_EQ(0, close(thread_pipe[1]));
293}
294
295TEST(signal_scoping_thread_after)
296{
297	pthread_t scoped_thread;
298	enum thread_return ret = THREAD_INVALID;
299	int thread_pipe[2];
300
301	drop_caps(_metadata);
302	ASSERT_EQ(0, pipe2(thread_pipe, O_CLOEXEC));
303
304	/* Enforces restriction before creating the thread. */
305	create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
306
307	ASSERT_EQ(0, pthread_create(&scoped_thread, NULL, thread_sync,
308				    &thread_pipe[0]));
309
310	EXPECT_EQ(0, pthread_kill(scoped_thread, 0));
311	EXPECT_EQ(1, write(thread_pipe[1], ".", 1));
312
313	EXPECT_EQ(0, pthread_join(scoped_thread, (void **)&ret));
314	EXPECT_EQ(THREAD_SUCCESS, ret);
315
316	EXPECT_EQ(0, close(thread_pipe[0]));
317	EXPECT_EQ(0, close(thread_pipe[1]));
318}
319
320struct thread_setuid_args {
321	int pipe_read, new_uid;
322};
323
324void *thread_setuid(void *ptr)
325{
326	const struct thread_setuid_args *arg = ptr;
327	char buf;
328
329	if (read(arg->pipe_read, &buf, 1) != 1)
330		return (void *)THREAD_ERROR;
331
332	/* libc's setuid() should update all thread's credentials. */
333	if (getuid() != arg->new_uid)
334		return (void *)THREAD_TEST_FAILED;
335
336	return (void *)THREAD_SUCCESS;
337}
338
339TEST(signal_scoping_thread_setuid)
340{
341	struct thread_setuid_args arg;
342	pthread_t no_sandbox_thread;
343	enum thread_return ret = THREAD_INVALID;
344	int pipe_parent[2];
345	int prev_uid;
346
347	disable_caps(_metadata);
348
349	/* This test does not need to be run as root. */
350	prev_uid = getuid();
351	arg.new_uid = prev_uid + 1;
352	EXPECT_LT(0, arg.new_uid);
353
354	ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
355	arg.pipe_read = pipe_parent[0];
356
357	/* Capabilities must be set before creating a new thread. */
358	set_cap(_metadata, CAP_SETUID);
359	ASSERT_EQ(0, pthread_create(&no_sandbox_thread, NULL, thread_setuid,
360				    &arg));
361
362	/* Enforces restriction after creating the thread. */
363	create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
364
365	EXPECT_NE(arg.new_uid, getuid());
366	EXPECT_EQ(0, setuid(arg.new_uid));
367	EXPECT_EQ(arg.new_uid, getuid());
368	EXPECT_EQ(1, write(pipe_parent[1], ".", 1));
369
370	EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret));
371	EXPECT_EQ(THREAD_SUCCESS, ret);
372
373	clear_cap(_metadata, CAP_SETUID);
374	EXPECT_EQ(0, close(pipe_parent[0]));
375	EXPECT_EQ(0, close(pipe_parent[1]));
376}
377
378const short backlog = 10;
379
380static volatile sig_atomic_t signal_received;
381
382static void handle_sigurg(int sig)
383{
384	if (sig == SIGURG)
385		signal_received = 1;
386	else
387		signal_received = -1;
388}
389
390static int setup_signal_handler(int signal)
391{
392	struct sigaction sa = {
393		.sa_handler = handle_sigurg,
394	};
395
396	if (sigemptyset(&sa.sa_mask))
397		return -1;
398
399	sa.sa_flags = SA_SIGINFO | SA_RESTART;
400	return sigaction(SIGURG, &sa, NULL);
401}
402
403/* clang-format off */
404FIXTURE(fown) {};
405/* clang-format on */
406
407enum fown_sandbox {
408	SANDBOX_NONE,
409	SANDBOX_BEFORE_FORK,
410	SANDBOX_BEFORE_SETOWN,
411	SANDBOX_AFTER_SETOWN,
412};
413
414FIXTURE_VARIANT(fown)
415{
416	const enum fown_sandbox sandbox_setown;
417};
418
419/* clang-format off */
420FIXTURE_VARIANT_ADD(fown, no_sandbox) {
421	/* clang-format on */
422	.sandbox_setown = SANDBOX_NONE,
423};
424
425/* clang-format off */
426FIXTURE_VARIANT_ADD(fown, sandbox_before_fork) {
427	/* clang-format on */
428	.sandbox_setown = SANDBOX_BEFORE_FORK,
429};
430
431/* clang-format off */
432FIXTURE_VARIANT_ADD(fown, sandbox_before_setown) {
433	/* clang-format on */
434	.sandbox_setown = SANDBOX_BEFORE_SETOWN,
435};
436
437/* clang-format off */
438FIXTURE_VARIANT_ADD(fown, sandbox_after_setown) {
439	/* clang-format on */
440	.sandbox_setown = SANDBOX_AFTER_SETOWN,
441};
442
443FIXTURE_SETUP(fown)
444{
445	drop_caps(_metadata);
446}
447
448FIXTURE_TEARDOWN(fown)
449{
450}
451
452/*
453 * Sending an out of bound message will trigger the SIGURG signal
454 * through file_send_sigiotask.
455 */
456TEST_F(fown, sigurg_socket)
457{
458	int server_socket, recv_socket;
459	struct service_fixture server_address;
460	char buffer_parent;
461	int status;
462	int pipe_parent[2], pipe_child[2];
463	pid_t child;
464
465	memset(&server_address, 0, sizeof(server_address));
466	set_unix_address(&server_address, 0);
467
468	ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
469	ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC));
470
471	if (variant->sandbox_setown == SANDBOX_BEFORE_FORK)
472		create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
473
474	child = fork();
475	ASSERT_LE(0, child);
476	if (child == 0) {
477		int client_socket;
478		char buffer_child;
479
480		EXPECT_EQ(0, close(pipe_parent[1]));
481		EXPECT_EQ(0, close(pipe_child[0]));
482
483		ASSERT_EQ(0, setup_signal_handler(SIGURG));
484		client_socket = socket(AF_UNIX, SOCK_STREAM, 0);
485		ASSERT_LE(0, client_socket);
486
487		/* Waits for the parent to listen. */
488		ASSERT_EQ(1, read(pipe_parent[0], &buffer_child, 1));
489		ASSERT_EQ(0, connect(client_socket, &server_address.unix_addr,
490				     server_address.unix_addr_len));
491
492		/*
493		 * Waits for the parent to accept the connection, sandbox
494		 * itself, and call fcntl(2).
495		 */
496		ASSERT_EQ(1, read(pipe_parent[0], &buffer_child, 1));
497		/* May signal itself. */
498		ASSERT_EQ(1, send(client_socket, ".", 1, MSG_OOB));
499		EXPECT_EQ(0, close(client_socket));
500		ASSERT_EQ(1, write(pipe_child[1], ".", 1));
501		EXPECT_EQ(0, close(pipe_child[1]));
502
503		/* Waits for the message to be received. */
504		ASSERT_EQ(1, read(pipe_parent[0], &buffer_child, 1));
505		EXPECT_EQ(0, close(pipe_parent[0]));
506
507		if (variant->sandbox_setown == SANDBOX_BEFORE_SETOWN) {
508			ASSERT_EQ(0, signal_received);
509		} else {
510			/*
511			 * A signal is only received if fcntl(F_SETOWN) was
512			 * called before any sandboxing or if the signal
513			 * receiver is in the same domain.
514			 */
515			ASSERT_EQ(1, signal_received);
516		}
517		_exit(_metadata->exit_code);
518		return;
519	}
520	EXPECT_EQ(0, close(pipe_parent[0]));
521	EXPECT_EQ(0, close(pipe_child[1]));
522
523	server_socket = socket(AF_UNIX, SOCK_STREAM, 0);
524	ASSERT_LE(0, server_socket);
525	ASSERT_EQ(0, bind(server_socket, &server_address.unix_addr,
526			  server_address.unix_addr_len));
527	ASSERT_EQ(0, listen(server_socket, backlog));
528	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
529
530	recv_socket = accept(server_socket, NULL, NULL);
531	ASSERT_LE(0, recv_socket);
532
533	if (variant->sandbox_setown == SANDBOX_BEFORE_SETOWN)
534		create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
535
536	/*
537	 * Sets the child to receive SIGURG for MSG_OOB.  This uncommon use is
538	 * a valid attack scenario which also simplifies this test.
539	 */
540	ASSERT_EQ(0, fcntl(recv_socket, F_SETOWN, child));
541
542	if (variant->sandbox_setown == SANDBOX_AFTER_SETOWN)
543		create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
544
545	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
546
547	/* Waits for the child to send MSG_OOB. */
548	ASSERT_EQ(1, read(pipe_child[0], &buffer_parent, 1));
549	EXPECT_EQ(0, close(pipe_child[0]));
550	ASSERT_EQ(1, recv(recv_socket, &buffer_parent, 1, MSG_OOB));
551	EXPECT_EQ(0, close(recv_socket));
552	EXPECT_EQ(0, close(server_socket));
553	ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
554	EXPECT_EQ(0, close(pipe_parent[1]));
555
556	ASSERT_EQ(child, waitpid(child, &status, 0));
557	if (WIFSIGNALED(status) || !WIFEXITED(status) ||
558	    WEXITSTATUS(status) != EXIT_SUCCESS)
559		_metadata->exit_code = KSFT_FAIL;
560}
561
562TEST_HARNESS_MAIN
~/ Read-only file preview.