Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/landlock: Test signal created by out-of-bound message

Add a test to verify that the SIGURG signal created by an out-of-bound
message in UNIX sockets is well controlled by the file_send_sigiotask
hook.

Test coverage for security/landlock is 92.2% of 1046 lines according to
gcc/gcov-14.

Signed-off-by: Tahera Fahimi <fahimitahera@gmail.com>
Link: https://lore.kernel.org/r/50daeed4d4f60d71e9564d0f24004a373fc5f7d5.1725657728.git.fahimitahera@gmail.com
[mic: Improve commit message and add test coverage, improve test with
four variants to fully cover the hook, use abstract unix socket to avoid
managing a file, use dedicated variable per process, add comments, avoid
negative ASSERT, move close calls]
Co-developed-by: Mickaël Salaün <mic@digikod.net>
Signed-off-by: Mickaël Salaün <mic@digikod.net>

authored by

Tahera Fahimi and committed by
Mickaël Salaün
f34e9ce5 c8994965

+184
+184
tools/testing/selftests/landlock/scoped_signal_test.c
··· 297 297 EXPECT_EQ(0, close(thread_pipe[1])); 298 298 } 299 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 + 300 484 TEST_HARNESS_MAIN