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

selftests/coredump: add tests for AF_UNIX coredumps

Add a simple test for generating coredumps via AF_UNIX sockets.

Link: https://lore.kernel.org/20250516-work-coredump-socket-v8-9-664f3caf2516@kernel.org
Acked-by: Luca Boccassi <luca.boccassi@gmail.com>
Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
Signed-off-by: Christian Brauner <brauner@kernel.org>

+466 -1
+466 -1
tools/testing/selftests/coredump/stackdump_test.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 3 #include <fcntl.h> 4 + #include <inttypes.h> 4 5 #include <libgen.h> 5 6 #include <linux/limits.h> 6 7 #include <pthread.h> 7 8 #include <string.h> 9 + #include <sys/mount.h> 8 10 #include <sys/resource.h> 11 + #include <sys/stat.h> 12 + #include <sys/socket.h> 13 + #include <sys/un.h> 9 14 #include <unistd.h> 10 15 11 16 #include "../kselftest_harness.h" 17 + #include "../pidfd/pidfd.h" 12 18 13 19 #define STACKDUMP_FILE "stack_values" 14 20 #define STACKDUMP_SCRIPT "stackdump" ··· 41 35 FIXTURE(coredump) 42 36 { 43 37 char original_core_pattern[256]; 38 + pid_t pid_coredump_server; 44 39 }; 45 40 46 41 FIXTURE_SETUP(coredump) ··· 51 44 char *dir; 52 45 int ret; 53 46 47 + self->pid_coredump_server = -ESRCH; 54 48 file = fopen("/proc/sys/kernel/core_pattern", "r"); 55 49 ASSERT_NE(NULL, file); 56 50 ··· 69 61 { 70 62 const char *reason; 71 63 FILE *file; 72 - int ret; 64 + int ret, status; 73 65 74 66 unlink(STACKDUMP_FILE); 67 + 68 + if (self->pid_coredump_server > 0) { 69 + kill(self->pid_coredump_server, SIGTERM); 70 + waitpid(self->pid_coredump_server, &status, 0); 71 + } 72 + unlink("/tmp/coredump.file"); 73 + unlink("/tmp/coredump.socket"); 75 74 76 75 file = fopen("/proc/sys/kernel/core_pattern", "w"); 77 76 if (!file) { ··· 167 152 ASSERT_EQ(i, 1 + NUM_THREAD_SPAWN); 168 153 169 154 fclose(file); 155 + } 156 + 157 + TEST_F(coredump, socket) 158 + { 159 + int fd, pidfd, ret, status; 160 + FILE *file; 161 + pid_t pid, pid_coredump_server; 162 + struct stat st; 163 + char core_file[PATH_MAX]; 164 + struct pidfd_info info = {}; 165 + int ipc_sockets[2]; 166 + char c; 167 + const struct sockaddr_un coredump_sk = { 168 + .sun_family = AF_UNIX, 169 + .sun_path = "/tmp/coredump.socket", 170 + }; 171 + size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) + 172 + sizeof("/tmp/coredump.socket"); 173 + 174 + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 175 + ASSERT_EQ(ret, 0); 176 + 177 + file = fopen("/proc/sys/kernel/core_pattern", "w"); 178 + ASSERT_NE(file, NULL); 179 + 180 + ret = fprintf(file, "@/tmp/coredump.socket"); 181 + ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); 182 + ASSERT_EQ(fclose(file), 0); 183 + 184 + pid_coredump_server = fork(); 185 + ASSERT_GE(pid_coredump_server, 0); 186 + if (pid_coredump_server == 0) { 187 + int fd_server, fd_coredump, fd_peer_pidfd, fd_core_file; 188 + socklen_t fd_peer_pidfd_len; 189 + 190 + close(ipc_sockets[0]); 191 + 192 + fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 193 + if (fd_server < 0) 194 + _exit(EXIT_FAILURE); 195 + 196 + ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len); 197 + if (ret < 0) { 198 + fprintf(stderr, "Failed to bind coredump socket\n"); 199 + close(fd_server); 200 + close(ipc_sockets[1]); 201 + _exit(EXIT_FAILURE); 202 + } 203 + 204 + ret = listen(fd_server, 1); 205 + if (ret < 0) { 206 + fprintf(stderr, "Failed to listen on coredump socket\n"); 207 + close(fd_server); 208 + close(ipc_sockets[1]); 209 + _exit(EXIT_FAILURE); 210 + } 211 + 212 + if (write_nointr(ipc_sockets[1], "1", 1) < 0) { 213 + close(fd_server); 214 + close(ipc_sockets[1]); 215 + _exit(EXIT_FAILURE); 216 + } 217 + 218 + close(ipc_sockets[1]); 219 + 220 + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); 221 + if (fd_coredump < 0) { 222 + fprintf(stderr, "Failed to accept coredump socket connection\n"); 223 + close(fd_server); 224 + _exit(EXIT_FAILURE); 225 + } 226 + 227 + fd_peer_pidfd_len = sizeof(fd_peer_pidfd); 228 + ret = getsockopt(fd_coredump, SOL_SOCKET, SO_PEERPIDFD, 229 + &fd_peer_pidfd, &fd_peer_pidfd_len); 230 + if (ret < 0) { 231 + fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n"); 232 + close(fd_coredump); 233 + close(fd_server); 234 + _exit(EXIT_FAILURE); 235 + } 236 + 237 + memset(&info, 0, sizeof(info)); 238 + info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; 239 + ret = ioctl(fd_peer_pidfd, PIDFD_GET_INFO, &info); 240 + if (ret < 0) { 241 + fprintf(stderr, "Failed to retrieve pidfd info from peer pidfd for coredump socket connection\n"); 242 + close(fd_coredump); 243 + close(fd_server); 244 + close(fd_peer_pidfd); 245 + _exit(EXIT_FAILURE); 246 + } 247 + 248 + if (!(info.mask & PIDFD_INFO_COREDUMP)) { 249 + fprintf(stderr, "Missing coredump information from coredumping task\n"); 250 + close(fd_coredump); 251 + close(fd_server); 252 + close(fd_peer_pidfd); 253 + _exit(EXIT_FAILURE); 254 + } 255 + 256 + if (!(info.coredump_mask & PIDFD_COREDUMPED)) { 257 + fprintf(stderr, "Received connection from non-coredumping task\n"); 258 + close(fd_coredump); 259 + close(fd_server); 260 + close(fd_peer_pidfd); 261 + _exit(EXIT_FAILURE); 262 + } 263 + 264 + fd_core_file = creat("/tmp/coredump.file", 0644); 265 + if (fd_core_file < 0) { 266 + fprintf(stderr, "Failed to create coredump file\n"); 267 + close(fd_coredump); 268 + close(fd_server); 269 + close(fd_peer_pidfd); 270 + _exit(EXIT_FAILURE); 271 + } 272 + 273 + for (;;) { 274 + char buffer[4096]; 275 + ssize_t bytes_read, bytes_write; 276 + 277 + bytes_read = read(fd_coredump, buffer, sizeof(buffer)); 278 + if (bytes_read < 0) { 279 + close(fd_coredump); 280 + close(fd_server); 281 + close(fd_peer_pidfd); 282 + close(fd_core_file); 283 + _exit(EXIT_FAILURE); 284 + } 285 + 286 + if (bytes_read == 0) 287 + break; 288 + 289 + bytes_write = write(fd_core_file, buffer, bytes_read); 290 + if (bytes_read != bytes_write) { 291 + close(fd_coredump); 292 + close(fd_server); 293 + close(fd_peer_pidfd); 294 + close(fd_core_file); 295 + _exit(EXIT_FAILURE); 296 + } 297 + } 298 + 299 + close(fd_coredump); 300 + close(fd_server); 301 + close(fd_peer_pidfd); 302 + close(fd_core_file); 303 + _exit(EXIT_SUCCESS); 304 + } 305 + self->pid_coredump_server = pid_coredump_server; 306 + 307 + EXPECT_EQ(close(ipc_sockets[1]), 0); 308 + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); 309 + EXPECT_EQ(close(ipc_sockets[0]), 0); 310 + 311 + pid = fork(); 312 + ASSERT_GE(pid, 0); 313 + if (pid == 0) 314 + crashing_child(); 315 + 316 + pidfd = sys_pidfd_open(pid, 0); 317 + ASSERT_GE(pidfd, 0); 318 + 319 + waitpid(pid, &status, 0); 320 + ASSERT_TRUE(WIFSIGNALED(status)); 321 + ASSERT_TRUE(WCOREDUMP(status)); 322 + 323 + info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; 324 + ASSERT_EQ(ioctl(pidfd, PIDFD_GET_INFO, &info), 0); 325 + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); 326 + ASSERT_GT((info.coredump_mask & PIDFD_COREDUMPED), 0); 327 + 328 + waitpid(pid_coredump_server, &status, 0); 329 + self->pid_coredump_server = -ESRCH; 330 + ASSERT_TRUE(WIFEXITED(status)); 331 + ASSERT_EQ(WEXITSTATUS(status), 0); 332 + 333 + ASSERT_EQ(stat("/tmp/coredump.file", &st), 0); 334 + ASSERT_GT(st.st_size, 0); 335 + /* 336 + * We should somehow validate the produced core file. 337 + * For now just allow for visual inspection 338 + */ 339 + system("file /tmp/coredump.file"); 340 + } 341 + 342 + TEST_F(coredump, socket_detect_userspace_client) 343 + { 344 + int fd, pidfd, ret, status; 345 + FILE *file; 346 + pid_t pid, pid_coredump_server; 347 + struct stat st; 348 + char core_file[PATH_MAX]; 349 + struct pidfd_info info = {}; 350 + int ipc_sockets[2]; 351 + char c; 352 + const struct sockaddr_un coredump_sk = { 353 + .sun_family = AF_UNIX, 354 + .sun_path = "/tmp/coredump.socket", 355 + }; 356 + size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) + 357 + sizeof("/tmp/coredump.socket"); 358 + 359 + file = fopen("/proc/sys/kernel/core_pattern", "w"); 360 + ASSERT_NE(file, NULL); 361 + 362 + ret = fprintf(file, "@/tmp/coredump.socket"); 363 + ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); 364 + ASSERT_EQ(fclose(file), 0); 365 + 366 + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 367 + ASSERT_EQ(ret, 0); 368 + 369 + pid_coredump_server = fork(); 370 + ASSERT_GE(pid_coredump_server, 0); 371 + if (pid_coredump_server == 0) { 372 + int fd_server, fd_coredump, fd_peer_pidfd, fd_core_file; 373 + socklen_t fd_peer_pidfd_len; 374 + 375 + close(ipc_sockets[0]); 376 + 377 + fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 378 + if (fd_server < 0) 379 + _exit(EXIT_FAILURE); 380 + 381 + ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len); 382 + if (ret < 0) { 383 + fprintf(stderr, "Failed to bind coredump socket\n"); 384 + close(fd_server); 385 + close(ipc_sockets[1]); 386 + _exit(EXIT_FAILURE); 387 + } 388 + 389 + ret = listen(fd_server, 1); 390 + if (ret < 0) { 391 + fprintf(stderr, "Failed to listen on coredump socket\n"); 392 + close(fd_server); 393 + close(ipc_sockets[1]); 394 + _exit(EXIT_FAILURE); 395 + } 396 + 397 + if (write_nointr(ipc_sockets[1], "1", 1) < 0) { 398 + close(fd_server); 399 + close(ipc_sockets[1]); 400 + _exit(EXIT_FAILURE); 401 + } 402 + 403 + close(ipc_sockets[1]); 404 + 405 + fd_coredump = accept4(fd_server, NULL, NULL, SOCK_CLOEXEC); 406 + if (fd_coredump < 0) { 407 + fprintf(stderr, "Failed to accept coredump socket connection\n"); 408 + close(fd_server); 409 + _exit(EXIT_FAILURE); 410 + } 411 + 412 + fd_peer_pidfd_len = sizeof(fd_peer_pidfd); 413 + ret = getsockopt(fd_coredump, SOL_SOCKET, SO_PEERPIDFD, 414 + &fd_peer_pidfd, &fd_peer_pidfd_len); 415 + if (ret < 0) { 416 + fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n"); 417 + close(fd_coredump); 418 + close(fd_server); 419 + _exit(EXIT_FAILURE); 420 + } 421 + 422 + memset(&info, 0, sizeof(info)); 423 + info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; 424 + ret = ioctl(fd_peer_pidfd, PIDFD_GET_INFO, &info); 425 + if (ret < 0) { 426 + fprintf(stderr, "Failed to retrieve pidfd info from peer pidfd for coredump socket connection\n"); 427 + close(fd_coredump); 428 + close(fd_server); 429 + close(fd_peer_pidfd); 430 + _exit(EXIT_FAILURE); 431 + } 432 + 433 + if (!(info.mask & PIDFD_INFO_COREDUMP)) { 434 + fprintf(stderr, "Missing coredump information from coredumping task\n"); 435 + close(fd_coredump); 436 + close(fd_server); 437 + close(fd_peer_pidfd); 438 + _exit(EXIT_FAILURE); 439 + } 440 + 441 + if (info.coredump_mask & PIDFD_COREDUMPED) { 442 + fprintf(stderr, "Received unexpected connection from coredumping task\n"); 443 + close(fd_coredump); 444 + close(fd_server); 445 + close(fd_peer_pidfd); 446 + _exit(EXIT_FAILURE); 447 + } 448 + 449 + close(fd_coredump); 450 + close(fd_server); 451 + close(fd_peer_pidfd); 452 + close(fd_core_file); 453 + _exit(EXIT_SUCCESS); 454 + } 455 + self->pid_coredump_server = pid_coredump_server; 456 + 457 + EXPECT_EQ(close(ipc_sockets[1]), 0); 458 + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); 459 + EXPECT_EQ(close(ipc_sockets[0]), 0); 460 + 461 + pid = fork(); 462 + ASSERT_GE(pid, 0); 463 + if (pid == 0) { 464 + int fd_socket; 465 + ssize_t ret; 466 + 467 + fd_socket = socket(AF_UNIX, SOCK_STREAM, 0); 468 + if (fd_socket < 0) 469 + _exit(EXIT_FAILURE); 470 + 471 + 472 + ret = connect(fd_socket, (const struct sockaddr *)&coredump_sk, coredump_sk_len); 473 + if (ret < 0) 474 + _exit(EXIT_FAILURE); 475 + 476 + (void *)write(fd_socket, &(char){ 0 }, 1); 477 + close(fd_socket); 478 + _exit(EXIT_SUCCESS); 479 + } 480 + 481 + pidfd = sys_pidfd_open(pid, 0); 482 + ASSERT_GE(pidfd, 0); 483 + 484 + waitpid(pid, &status, 0); 485 + ASSERT_TRUE(WIFEXITED(status)); 486 + ASSERT_EQ(WEXITSTATUS(status), 0); 487 + 488 + info.mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP; 489 + ASSERT_EQ(ioctl(pidfd, PIDFD_GET_INFO, &info), 0); 490 + ASSERT_GT((info.mask & PIDFD_INFO_COREDUMP), 0); 491 + ASSERT_EQ((info.coredump_mask & PIDFD_COREDUMPED), 0); 492 + 493 + waitpid(pid_coredump_server, &status, 0); 494 + self->pid_coredump_server = -ESRCH; 495 + ASSERT_TRUE(WIFEXITED(status)); 496 + ASSERT_EQ(WEXITSTATUS(status), 0); 497 + 498 + ASSERT_NE(stat("/tmp/coredump.file", &st), 0); 499 + ASSERT_EQ(errno, ENOENT); 500 + } 501 + 502 + TEST_F(coredump, socket_enoent) 503 + { 504 + int pidfd, ret, status; 505 + FILE *file; 506 + pid_t pid; 507 + char core_file[PATH_MAX]; 508 + 509 + file = fopen("/proc/sys/kernel/core_pattern", "w"); 510 + ASSERT_NE(file, NULL); 511 + 512 + ret = fprintf(file, "@/tmp/coredump.socket"); 513 + ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); 514 + ASSERT_EQ(fclose(file), 0); 515 + 516 + pid = fork(); 517 + ASSERT_GE(pid, 0); 518 + if (pid == 0) 519 + crashing_child(); 520 + 521 + pidfd = sys_pidfd_open(pid, 0); 522 + ASSERT_GE(pidfd, 0); 523 + 524 + waitpid(pid, &status, 0); 525 + ASSERT_TRUE(WIFSIGNALED(status)); 526 + ASSERT_FALSE(WCOREDUMP(status)); 527 + } 528 + 529 + TEST_F(coredump, socket_no_listener) 530 + { 531 + int pidfd, ret, status; 532 + FILE *file; 533 + pid_t pid, pid_coredump_server; 534 + int ipc_sockets[2]; 535 + char c; 536 + const struct sockaddr_un coredump_sk = { 537 + .sun_family = AF_UNIX, 538 + .sun_path = "/tmp/coredump.socket", 539 + }; 540 + size_t coredump_sk_len = offsetof(struct sockaddr_un, sun_path) + 541 + sizeof("/tmp/coredump.socket"); 542 + 543 + ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); 544 + ASSERT_EQ(ret, 0); 545 + 546 + file = fopen("/proc/sys/kernel/core_pattern", "w"); 547 + ASSERT_NE(file, NULL); 548 + 549 + ret = fprintf(file, "@/tmp/coredump.socket"); 550 + ASSERT_EQ(ret, strlen("@/tmp/coredump.socket")); 551 + ASSERT_EQ(fclose(file), 0); 552 + 553 + pid_coredump_server = fork(); 554 + ASSERT_GE(pid_coredump_server, 0); 555 + if (pid_coredump_server == 0) { 556 + int fd_server; 557 + socklen_t fd_peer_pidfd_len; 558 + 559 + close(ipc_sockets[0]); 560 + 561 + fd_server = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 562 + if (fd_server < 0) 563 + _exit(EXIT_FAILURE); 564 + 565 + ret = bind(fd_server, (const struct sockaddr *)&coredump_sk, coredump_sk_len); 566 + if (ret < 0) { 567 + fprintf(stderr, "Failed to bind coredump socket\n"); 568 + close(fd_server); 569 + close(ipc_sockets[1]); 570 + _exit(EXIT_FAILURE); 571 + } 572 + 573 + if (write_nointr(ipc_sockets[1], "1", 1) < 0) { 574 + close(fd_server); 575 + close(ipc_sockets[1]); 576 + _exit(EXIT_FAILURE); 577 + } 578 + 579 + close(fd_server); 580 + close(ipc_sockets[1]); 581 + _exit(EXIT_SUCCESS); 582 + } 583 + self->pid_coredump_server = pid_coredump_server; 584 + 585 + EXPECT_EQ(close(ipc_sockets[1]), 0); 586 + ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); 587 + EXPECT_EQ(close(ipc_sockets[0]), 0); 588 + 589 + pid = fork(); 590 + ASSERT_GE(pid, 0); 591 + if (pid == 0) 592 + crashing_child(); 593 + 594 + pidfd = sys_pidfd_open(pid, 0); 595 + ASSERT_GE(pidfd, 0); 596 + 597 + waitpid(pid, &status, 0); 598 + ASSERT_TRUE(WIFSIGNALED(status)); 599 + ASSERT_FALSE(WCOREDUMP(status)); 600 + 601 + waitpid(pid_coredump_server, &status, 0); 602 + self->pid_coredump_server = -ESRCH; 603 + ASSERT_TRUE(WIFEXITED(status)); 604 + ASSERT_EQ(WEXITSTATUS(status), 0); 170 605 } 171 606 172 607 TEST_HARNESS_MAIN