~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/landlock/scoped_signal_test.c

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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. */
 23 static volatile sig_atomic_t is_signaled;
 24 
 25 /* clang-format off */
 26 FIXTURE(scoping_signals) {};
 27 /* clang-format on */
 28 
 29 FIXTURE_VARIANT(scoping_signals)
 30 {
 31         int sig;
 32 };
 33 
 34 /* clang-format off */
 35 FIXTURE_VARIANT_ADD(scoping_signals, sigtrap) {
 36         /* clang-format on */
 37         .sig = SIGTRAP,
 38 };
 39 
 40 /* clang-format off */
 41 FIXTURE_VARIANT_ADD(scoping_signals, sigurg) {
 42         /* clang-format on */
 43         .sig = SIGURG,
 44 };
 45 
 46 /* clang-format off */
 47 FIXTURE_VARIANT_ADD(scoping_signals, sighup) {
 48         /* clang-format on */
 49         .sig = SIGHUP,
 50 };
 51 
 52 /* clang-format off */
 53 FIXTURE_VARIANT_ADD(scoping_signals, sigtstp) {
 54         /* clang-format on */
 55         .sig = SIGTSTP,
 56 };
 57 
 58 FIXTURE_SETUP(scoping_signals)
 59 {
 60         drop_caps(_metadata);
 61 
 62         is_signaled = 0;
 63 }
 64 
 65 FIXTURE_TEARDOWN(scoping_signals)
 66 {
 67 }
 68 
 69 static 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  */
 79 TEST_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 */
154 FIXTURE(scoped_domains) {};
155 /* clang-format on */
156 
157 #include "scoped_base_variants.h"
158 
159 FIXTURE_SETUP(scoped_domains)
160 {
161         drop_caps(_metadata);
162 }
163 
164 FIXTURE_TEARDOWN(scoped_domains)
165 {
166 }
167 
168 /*
169  * This test ensures that a scoped process cannot send signal out of
170  * scoped domain.
171  */
172 TEST_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 
252 static int thread_pipe[2];
253 
254 enum thread_return {
255         THREAD_INVALID = 0,
256         THREAD_SUCCESS = 1,
257         THREAD_ERROR = 2,
258 };
259 
260 void *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 
270 TEST(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 
300 const short backlog = 10;
301 
302 static volatile sig_atomic_t signal_received;
303 
304 static void handle_sigurg(int sig)
305 {
306         if (sig == SIGURG)
307                 signal_received = 1;
308         else
309                 signal_received = -1;
310 }
311 
312 static 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 */
326 FIXTURE(fown) {};
327 /* clang-format on */
328 
329 enum fown_sandbox {
330         SANDBOX_NONE,
331         SANDBOX_BEFORE_FORK,
332         SANDBOX_BEFORE_SETOWN,
333         SANDBOX_AFTER_SETOWN,
334 };
335 
336 FIXTURE_VARIANT(fown)
337 {
338         const enum fown_sandbox sandbox_setown;
339 };
340 
341 /* clang-format off */
342 FIXTURE_VARIANT_ADD(fown, no_sandbox) {
343         /* clang-format on */
344         .sandbox_setown = SANDBOX_NONE,
345 };
346 
347 /* clang-format off */
348 FIXTURE_VARIANT_ADD(fown, sandbox_before_fork) {
349         /* clang-format on */
350         .sandbox_setown = SANDBOX_BEFORE_FORK,
351 };
352 
353 /* clang-format off */
354 FIXTURE_VARIANT_ADD(fown, sandbox_before_setown) {
355         /* clang-format on */
356         .sandbox_setown = SANDBOX_BEFORE_SETOWN,
357 };
358 
359 /* clang-format off */
360 FIXTURE_VARIANT_ADD(fown, sandbox_after_setown) {
361         /* clang-format on */
362         .sandbox_setown = SANDBOX_AFTER_SETOWN,
363 };
364 
365 FIXTURE_SETUP(fown)
366 {
367         drop_caps(_metadata);
368 }
369 
370 FIXTURE_TEARDOWN(fown)
371 {
372 }
373 
374 /*
375  * Sending an out of bound message will trigger the SIGURG signal
376  * through file_send_sigiotask.
377  */
378 TEST_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 
484 TEST_HARNESS_MAIN
485 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php