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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.