Linux Audio

Check our new training course

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