1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <fcntl.h> 4 #include <inttypes.h> 5 #include <libgen.h> 6 #include <linux/limits.h> 7 #include <pthread.h> 8 #include <string.h> 9 #include <sys/mount.h> 10 #include <sys/resource.h> 11 #include <sys/stat.h> 12 #include <sys/socket.h> 13 #include <sys/un.h> 14 #include <unistd.h> 15 16 #include "../kselftest_harness.h" 17 #include "../pidfd/pidfd.h" 18 19 #define STACKDUMP_FILE "stack_values" 20 #define STACKDUMP_SCRIPT "stackdump" 21 #define NUM_THREAD_SPAWN 128 22 23 static void *do_nothing(void *) 24 { 25 while (1) 26 pause(); 27 } 28 29 static void crashing_child(void) 30 { 31 pthread_t thread; 32 int i; 33 34 for (i = 0; i < NUM_THREAD_SPAWN; ++i) 35 pthread_create(&thread, NULL, do_nothing, NULL); 36 37 /* crash on purpose */ 38 i = *(int *)NULL; 39 } 40 41 FIXTURE(coredump) 42 { 43 char original_core_pattern[256]; 44 pid_t pid_coredump_server; 45 }; 46 47 FIXTURE_SETUP(coredump) 48 { 49 char buf[PATH_MAX]; 50 FILE *file; 51 char *dir; 52 int ret; 53 54 self->pid_coredump_server = -ESRCH; 55 file = fopen("/proc/sys/kernel/core_pattern", "r"); 56 ASSERT_NE(NULL, file); 57 58 ret = fread(self->original_core_pattern, 1, sizeof(self->original_core_pattern), file); 59 ASSERT_TRUE(ret || feof(file)); 60 ASSERT_LT(ret, sizeof(self->original_core_pattern)); 61 62 self->original_core_pattern[ret] = '\0'; 63 64 ret = fclose(file); 65 ASSERT_EQ(0, ret); 66 } 67 68 FIXTURE_TEARDOWN(coredump) 69 { 70 const char *reason; 71 FILE *file; 72 int ret, status; 73 74 unlink(STACKDUMP_FILE); 75 76 if (self->pid_coredump_server > 0) { 77 kill(self->pid_coredump_server, SIGTERM); 78 waitpid(self->pid_coredump_server, &status, 0); 79 } 80 unlink("/tmp/coredump.file"); 81 unlink("/tmp/coredump.socket"); 82 83 file = fopen("/proc/sys/kernel/core_pattern", "w"); 84 if (!file) { 85 reason = "Unable to open core_pattern"; 86 goto fail; 87 } 88 89 ret = fprintf(file, "%s", self->original_core_pattern); 90 if (ret < 0) { 91 reason = "Unable to write to core_pattern"; 92 goto fail; 93 } 94 95 ret = fclose(file); 96 if (ret) { 97 reason = "Unable to close core_pattern"; 98 goto fail; 99 } 100 101 return; 102 fail: 103 /* This should never happen */ 104 fprintf(stderr, "Failed to cleanup stackdump test: %s\n", reason); 105 } 106 107 TEST_F_TIMEOUT(coredump, stackdump, 120) 108 { 109 struct sigaction action = {}; 110 unsigned long long stack; 111 char *test_dir, *line; 112 size_t line_length; 113 char buf[PATH_MAX]; 114 int ret, i, status; 115 FILE *file; 116 pid_t pid; 117 118 /* 119 * Step 1: Setup core_pattern so that the stackdump script is executed when the child 120 * process crashes 121 */ 122 ret = readlink("/proc/self/exe", buf, sizeof(buf)); 123 ASSERT_NE(-1, ret); 124 ASSERT_LT(ret, sizeof(buf)); 125 buf[ret] = '\0'; 126 127 test_dir = dirname(buf); 128 129 file = fopen("/proc/sys/kernel/core_pattern", "w"); 130 ASSERT_NE(NULL, file); 131 132 ret = fprintf(file, "|%1$s/%2$s %%P %1$s/%3$s", test_dir, STACKDUMP_SCRIPT, STACKDUMP_FILE); 133 ASSERT_LT(0, ret); 134 135 ret = fclose(file); 136 ASSERT_EQ(0, ret); 137 138 /* Step 2: Create a process who spawns some threads then crashes */ 139 pid = fork(); 140 ASSERT_TRUE(pid >= 0); 141 if (pid == 0) 142 crashing_child(); 143 144 /* 145 * Step 3: Wait for the stackdump script to write the stack pointers to the stackdump file 146 */ 147 waitpid(pid, &status, 0); 148 ASSERT_TRUE(WIFSIGNALED(status)); 149 ASSERT_TRUE(WCOREDUMP(status)); 150 151 for (i = 0; i < 10; ++i) { 152 file = fopen(STACKDUMP_FILE, "r"); 153 if (file) 154 break; 155 sleep(1); 156 } 157 ASSERT_NE(file, NULL); 158 159 /* Step 4: Make sure all stack pointer values are non-zero */ 160 line = NULL; 161 for (i = 0; -1 != getline(&line, &line_length, file); ++i) { 162 stack = strtoull(line, NULL, 10); 163 ASSERT_NE(stack, 0); 164 } 165 free(line); 166 167 ASSERT_EQ(i, 1 + NUM_THREAD_SPAWN); 168 169 fclose(file); 170 } 171 172 TEST_F(coredump, socket) 173 { 174 int fd, pidfd, ret, status; 175 FILE *file; 176 pid_t pid, pid_coredump_server; 177 struct stat st; 178 char core_file[PATH_MAX]; 179 struct pidfd_info info = {}; 180 int ipc_sockets[2]; 181 char c; 182 const struct sockaddr_un coredump_sk = { 183 .sun_family = AF_UNIX, 184 .sun_path = "/tmp/coredump.socket", 185 }; 186 size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) + 187 sizeof("/tmp/coredump.socket"); 188 189 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 190 ASSERT_EQ(ret, 0); 191 192 file = fopen("/proc/sys/kernel/core_pattern", "w"); 193 ASSERT_NE(file, NULL); 194 195 ret = fprintf(file, "@/tmp/coredump.socket"); 196 ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); 197 ASSERT_EQ(fclose(file), 0); 198 199 pid_coredump_server = fork(); 200 ASSERT_GE(pid_coredump_server, 0); 201 if (pid_coredump_server == 0) { 202 int fd_server, fd_coredump, fd_peer_pidfd, fd_core_file; 203 socklen_t fd_peer_pidfd_len; 204 205 close(ipc_sockets[0]); 206 207 fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 208 if (fd_server < 0) 209 _exit(EXIT_FAILURE); 210 211 ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len); 212 if (ret < 0) { 213 fprintf(stderr, "Failed to bind coredump socket\n"); 214 close(fd_server); 215 close(ipc_sockets[1]); 216 _exit(EXIT_FAILURE); 217 } 218 219 ret = listen(fd_server, 1); 220 if (ret < 0) { 221 fprintf(stderr, "Failed to listen on coredump socket\n"); 222 close(fd_server); 223 close(ipc_sockets[1]); 224 _exit(EXIT_FAILURE); 225 } 226 227 if (write_nointr(ipc_sockets[1], "1", 1) < 0) { 228 close(fd_server); 229 close(ipc_sockets[1]); 230 _exit(EXIT_FAILURE); 231 } 232 233 close(ipc_sockets[1]); 234 235 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); 236 if (fd_coredump < 0) { 237 fprintf(stderr, "Failed to accept coredump socket connection\n"); 238 close(fd_server); 239 _exit(EXIT_FAILURE); 240 } 241 242 fd_peer_pidfd_len = sizeof(fd_peer_pidfd); 243 ret = getsockopt(fd_coredump, SOL_SOCKET, SO_PEERPIDFD, 244 &fd_peer_pidfd, &fd_peer_pidfd_len); 245 if (ret < 0) { 246 fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n"); 247 close(fd_coredump); 248 close(fd_server); 249 _exit(EXIT_FAILURE); 250 } 251 252 memset(&info, 0, sizeof(info)); 253 info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; 254 ret = ioctl(fd_peer_pidfd, PIDFD_GET_INFO, &info); 255 if (ret < 0) { 256 fprintf(stderr, "Failed to retrieve pidfd info from peer pidfd for coredump socket connection\n"); 257 close(fd_coredump); 258 close(fd_server); 259 close(fd_peer_pidfd); 260 _exit(EXIT_FAILURE); 261 } 262 263 if (!(info.mask & PIDFD_INFO_COREDUMP)) { 264 fprintf(stderr, "Missing coredump information from coredumping task\n"); 265 close(fd_coredump); 266 close(fd_server); 267 close(fd_peer_pidfd); 268 _exit(EXIT_FAILURE); 269 } 270 271 if (!(info.coredump_mask & PIDFD_COREDUMPED)) { 272 fprintf(stderr, "Received connection from non-coredumping task\n"); 273 close(fd_coredump); 274 close(fd_server); 275 close(fd_peer_pidfd); 276 _exit(EXIT_FAILURE); 277 } 278 279 fd_core_file = creat("/tmp/coredump.file", 0644); 280 if (fd_core_file < 0) { 281 fprintf(stderr, "Failed to create coredump file\n"); 282 close(fd_coredump); 283 close(fd_server); 284 close(fd_peer_pidfd); 285 _exit(EXIT_FAILURE); 286 } 287 288 for (;;) { 289 char buffer[4096]; 290 ssize_t bytes_read, bytes_write; 291 292 bytes_read = read(fd_coredump, buffer, sizeof(buffer)); 293 if (bytes_read < 0) { 294 close(fd_coredump); 295 close(fd_server); 296 close(fd_peer_pidfd); 297 close(fd_core_file); 298 _exit(EXIT_FAILURE); 299 } 300 301 if (bytes_read == 0) 302 break; 303 304 bytes_write = write(fd_core_file, buffer, bytes_read); 305 if (bytes_read != bytes_write) { 306 close(fd_coredump); 307 close(fd_server); 308 close(fd_peer_pidfd); 309 close(fd_core_file); 310 _exit(EXIT_FAILURE); 311 } 312 } 313 314 close(fd_coredump); 315 close(fd_server); 316 close(fd_peer_pidfd); 317 close(fd_core_file); 318 _exit(EXIT_SUCCESS); 319 } 320 self->pid_coredump_server = pid_coredump_server; 321 322 EXPECT_EQ(close(ipc_sockets[1]), 0); 323 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); 324 EXPECT_EQ(close(ipc_sockets[0]), 0); 325 326 pid = fork(); 327 ASSERT_GE(pid, 0); 328 if (pid == 0) 329 crashing_child(); 330 331 pidfd = sys_pidfd_open(pid, 0); 332 ASSERT_GE(pidfd, 0); 333 334 waitpid(pid, &status, 0); 335 ASSERT_TRUE(WIFSIGNALED(status)); 336 ASSERT_TRUE(WCOREDUMP(status)); 337 338 info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; 339 ASSERT_EQ(ioctl(pidfd, PIDFD_GET_INFO, &info), 0); 340 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); 341 ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); 342 343 waitpid(pid_coredump_server, &status, 0); 344 self->pid_coredump_server = -ESRCH; 345 ASSERT_TRUE(WIFEXITED(status)); 346 ASSERT_EQ(WEXITSTATUS(status), 0); 347 348 ASSERT_EQ(stat("/tmp/coredump.file", &st), 0); 349 ASSERT_GT(st.st_size, 0); 350 /* 351 * We should somehow validate the produced core file. 352 * For now just allow for visual inspection 353 */ 354 system("file /tmp/coredump.file"); 355 } 356 357 TEST_F(coredump, socket_detect_userspace_client) 358 { 359 int fd, pidfd, ret, status; 360 FILE *file; 361 pid_t pid, pid_coredump_server; 362 struct stat st; 363 char core_file[PATH_MAX]; 364 struct pidfd_info info = {}; 365 int ipc_sockets[2]; 366 char c; 367 const struct sockaddr_un coredump_sk = { 368 .sun_family = AF_UNIX, 369 .sun_path = "/tmp/coredump.socket", 370 }; 371 size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) + 372 sizeof("/tmp/coredump.socket"); 373 374 file = fopen("/proc/sys/kernel/core_pattern", "w"); 375 ASSERT_NE(file, NULL); 376 377 ret = fprintf(file, "@/tmp/coredump.socket"); 378 ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); 379 ASSERT_EQ(fclose(file), 0); 380 381 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 382 ASSERT_EQ(ret, 0); 383 384 pid_coredump_server = fork(); 385 ASSERT_GE(pid_coredump_server, 0); 386 if (pid_coredump_server == 0) { 387 int fd_server, fd_coredump, fd_peer_pidfd, fd_core_file; 388 socklen_t fd_peer_pidfd_len; 389 390 close(ipc_sockets[0]); 391 392 fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 393 if (fd_server < 0) 394 _exit(EXIT_FAILURE); 395 396 ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len); 397 if (ret < 0) { 398 fprintf(stderr, "Failed to bind coredump socket\n"); 399 close(fd_server); 400 close(ipc_sockets[1]); 401 _exit(EXIT_FAILURE); 402 } 403 404 ret = listen(fd_server, 1); 405 if (ret < 0) { 406 fprintf(stderr, "Failed to listen on coredump socket\n"); 407 close(fd_server); 408 close(ipc_sockets[1]); 409 _exit(EXIT_FAILURE); 410 } 411 412 if (write_nointr(ipc_sockets[1], "1", 1) < 0) { 413 close(fd_server); 414 close(ipc_sockets[1]); 415 _exit(EXIT_FAILURE); 416 } 417 418 close(ipc_sockets[1]); 419 420 fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); 421 if (fd_coredump < 0) { 422 fprintf(stderr, "Failed to accept coredump socket connection\n"); 423 close(fd_server); 424 _exit(EXIT_FAILURE); 425 } 426 427 fd_peer_pidfd_len = sizeof(fd_peer_pidfd); 428 ret = getsockopt(fd_coredump, SOL_SOCKET, SO_PEERPIDFD, 429 &fd_peer_pidfd, &fd_peer_pidfd_len); 430 if (ret < 0) { 431 fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n"); 432 close(fd_coredump); 433 close(fd_server); 434 _exit(EXIT_FAILURE); 435 } 436 437 memset(&info, 0, sizeof(info)); 438 info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; 439 ret = ioctl(fd_peer_pidfd, PIDFD_GET_INFO, &info); 440 if (ret < 0) { 441 fprintf(stderr, "Failed to retrieve pidfd info from peer pidfd for coredump socket connection\n"); 442 close(fd_coredump); 443 close(fd_server); 444 close(fd_peer_pidfd); 445 _exit(EXIT_FAILURE); 446 } 447 448 if (!(info.mask & PIDFD_INFO_COREDUMP)) { 449 fprintf(stderr, "Missing coredump information from coredumping task\n"); 450 close(fd_coredump); 451 close(fd_server); 452 close(fd_peer_pidfd); 453 _exit(EXIT_FAILURE); 454 } 455 456 if (info.coredump_mask & PIDFD_COREDUMPED) { 457 fprintf(stderr, "Received unexpected connection from coredumping task\n"); 458 close(fd_coredump); 459 close(fd_server); 460 close(fd_peer_pidfd); 461 _exit(EXIT_FAILURE); 462 } 463 464 close(fd_coredump); 465 close(fd_server); 466 close(fd_peer_pidfd); 467 close(fd_core_file); 468 _exit(EXIT_SUCCESS); 469 } 470 self->pid_coredump_server = pid_coredump_server; 471 472 EXPECT_EQ(close(ipc_sockets[1]), 0); 473 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); 474 EXPECT_EQ(close(ipc_sockets[0]), 0); 475 476 pid = fork(); 477 ASSERT_GE(pid, 0); 478 if (pid == 0) { 479 int fd_socket; 480 ssize_t ret; 481 482 fd_socket = socket(AF_UNIX, SOCK_STREAM, 0); 483 if (fd_socket < 0) 484 _exit(EXIT_FAILURE); 485 486 487 ret = connect(fd_socket, (const struct sockaddr *)&coredump_sk, coredump_sk_len); 488 if (ret < 0) 489 _exit(EXIT_FAILURE); 490 491 (void *)write(fd_socket, &(char){ 0 }, 1); 492 close(fd_socket); 493 _exit(EXIT_SUCCESS); 494 } 495 496 pidfd = sys_pidfd_open(pid, 0); 497 ASSERT_GE(pidfd, 0); 498 499 waitpid(pid, &status, 0); 500 ASSERT_TRUE(WIFEXITED(status)); 501 ASSERT_EQ(WEXITSTATUS(status), 0); 502 503 info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; 504 ASSERT_EQ(ioctl(pidfd, PIDFD_GET_INFO, &info), 0); 505 ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); 506 ASSERT_EQ((info.coredump_mask & PIDFD_COREDUMPED), 0); 507 508 waitpid(pid_coredump_server, &status, 0); 509 self->pid_coredump_server = -ESRCH; 510 ASSERT_TRUE(WIFEXITED(status)); 511 ASSERT_EQ(WEXITSTATUS(status), 0); 512 513 ASSERT_NE(stat("/tmp/coredump.file", &st), 0); 514 ASSERT_EQ(errno, ENOENT); 515 } 516 517 TEST_F(coredump, socket_enoent) 518 { 519 int pidfd, ret, status; 520 FILE *file; 521 pid_t pid; 522 char core_file[PATH_MAX]; 523 524 file = fopen("/proc/sys/kernel/core_pattern", "w"); 525 ASSERT_NE(file, NULL); 526 527 ret = fprintf(file, "@/tmp/coredump.socket"); 528 ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); 529 ASSERT_EQ(fclose(file), 0); 530 531 pid = fork(); 532 ASSERT_GE(pid, 0); 533 if (pid == 0) 534 crashing_child(); 535 536 pidfd = sys_pidfd_open(pid, 0); 537 ASSERT_GE(pidfd, 0); 538 539 waitpid(pid, &status, 0); 540 ASSERT_TRUE(WIFSIGNALED(status)); 541 ASSERT_FALSE(WCOREDUMP(status)); 542 } 543 544 TEST_F(coredump, socket_no_listener) 545 { 546 int pidfd, ret, status; 547 FILE *file; 548 pid_t pid, pid_coredump_server; 549 int ipc_sockets[2]; 550 char c; 551 const struct sockaddr_un coredump_sk = { 552 .sun_family = AF_UNIX, 553 .sun_path = "/tmp/coredump.socket", 554 }; 555 size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) + 556 sizeof("/tmp/coredump.socket"); 557 558 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 559 ASSERT_EQ(ret, 0); 560 561 file = fopen("/proc/sys/kernel/core_pattern", "w"); 562 ASSERT_NE(file, NULL); 563 564 ret = fprintf(file, "@/tmp/coredump.socket"); 565 ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); 566 ASSERT_EQ(fclose(file), 0); 567 568 pid_coredump_server = fork(); 569 ASSERT_GE(pid_coredump_server, 0); 570 if (pid_coredump_server == 0) { 571 int fd_server; 572 socklen_t fd_peer_pidfd_len; 573 574 close(ipc_sockets[0]); 575 576 fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 577 if (fd_server < 0) 578 _exit(EXIT_FAILURE); 579 580 ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len); 581 if (ret < 0) { 582 fprintf(stderr, "Failed to bind coredump socket\n"); 583 close(fd_server); 584 close(ipc_sockets[1]); 585 _exit(EXIT_FAILURE); 586 } 587 588 if (write_nointr(ipc_sockets[1], "1", 1) < 0) { 589 close(fd_server); 590 close(ipc_sockets[1]); 591 _exit(EXIT_FAILURE); 592 } 593 594 close(fd_server); 595 close(ipc_sockets[1]); 596 _exit(EXIT_SUCCESS); 597 } 598 self->pid_coredump_server = pid_coredump_server; 599 600 EXPECT_EQ(close(ipc_sockets[1]), 0); 601 ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); 602 EXPECT_EQ(close(ipc_sockets[0]), 0); 603 604 pid = fork(); 605 ASSERT_GE(pid, 0); 606 if (pid == 0) 607 crashing_child(); 608 609 pidfd = sys_pidfd_open(pid, 0); 610 ASSERT_GE(pidfd, 0); 611 612 waitpid(pid, &status, 0); 613 ASSERT_TRUE(WIFSIGNALED(status)); 614 ASSERT_FALSE(WCOREDUMP(status)); 615 616 waitpid(pid_coredump_server, &status, 0); 617 self->pid_coredump_server = -ESRCH; 618 ASSERT_TRUE(WIFEXITED(status)); 619 ASSERT_EQ(WEXITSTATUS(status), 0); 620 } 621 622 TEST_HARNESS_MAIN 623