[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAJqdLroBTL42zoxKS3gb0NKBxjxG4evgEHMPr3AiEmz9kuW7tA@mail.gmail.com>
Date: Tue, 4 Nov 2025 10:11:35 +0100
From: Alexander Mikhalitsyn <alexander@...alicyn.com>
To: Christian Brauner <brauner@...nel.org>
Cc: linux-fsdevel@...r.kernel.org, Oleg Nesterov <oleg@...hat.com>,
Amir Goldstein <amir73il@...il.com>, Aleksa Sarai <cyphar@...har.com>,
Yu Watanabe <watanabe.yu+github@...il.com>, Josef Bacik <josef@...icpanda.com>,
Jeff Layton <jlayton@...nel.org>, Jann Horn <jannh@...gle.com>,
Luca Boccassi <luca.boccassi@...il.com>, linux-kernel@...r.kernel.org,
Alexander Viro <viro@...iv.linux.org.uk>, Jan Kara <jack@...e.cz>,
Lennart Poettering <lennart@...ttering.net>, Mike Yuan <me@...dnzj.com>,
Zbigniew Jędrzejewski-Szmek <zbyszek@...waw.pl>
Subject: Re: [PATCH 12/22] selftests/coredump: split out common helpers
Am Di., 28. Okt. 2025 um 09:46 Uhr schrieb Christian Brauner
<brauner@...nel.org>:
>
> into separate files.
>
> Signed-off-by: Christian Brauner <brauner@...nel.org>
Reviewed-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@...onical.com>
> ---
> tools/testing/selftests/coredump/coredump_test.h | 59 ++++
> .../selftests/coredump/coredump_test_helpers.c | 340 +++++++++++++++++++++
> 2 files changed, 399 insertions(+)
>
> diff --git a/tools/testing/selftests/coredump/coredump_test.h b/tools/testing/selftests/coredump/coredump_test.h
> new file mode 100644
> index 000000000000..ed47f01fa53c
> --- /dev/null
> +++ b/tools/testing/selftests/coredump/coredump_test.h
> @@ -0,0 +1,59 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#ifndef __COREDUMP_TEST_H
> +#define __COREDUMP_TEST_H
> +
> +#include <stdbool.h>
> +#include <sys/types.h>
> +#include <linux/coredump.h>
> +
> +#include "../kselftest_harness.h"
> +#include "../pidfd/pidfd.h"
> +
> +#ifndef PAGE_SIZE
> +#define PAGE_SIZE 4096
> +#endif
> +
> +#define NUM_THREAD_SPAWN 128
> +
> +/* Coredump fixture */
> +FIXTURE(coredump)
> +{
> + char original_core_pattern[256];
> + pid_t pid_coredump_server;
> + int fd_tmpfs_detached;
> +};
> +
> +/* Shared helper function declarations */
> +void *do_nothing(void *arg);
> +void crashing_child(void);
> +int create_detached_tmpfs(void);
> +int create_and_listen_unix_socket(const char *path);
> +bool set_core_pattern(const char *pattern);
> +int get_peer_pidfd(int fd);
> +bool get_pidfd_info(int fd_peer_pidfd, struct pidfd_info *info);
> +
> +/* Inline helper that uses harness types */
> +static inline void wait_and_check_coredump_server(pid_t pid_coredump_server,
> + struct __test_metadata *const _metadata,
> + FIXTURE_DATA(coredump) *self)
> +{
> + int status;
> + waitpid(pid_coredump_server, &status, 0);
> + self->pid_coredump_server = -ESRCH;
> + ASSERT_TRUE(WIFEXITED(status));
> + ASSERT_EQ(WEXITSTATUS(status), 0);
> +}
> +
> +/* Protocol helper function declarations */
> +ssize_t recv_marker(int fd);
> +bool read_marker(int fd, enum coredump_mark mark);
> +bool read_coredump_req(int fd, struct coredump_req *req);
> +bool send_coredump_ack(int fd, const struct coredump_req *req,
> + __u64 mask, size_t size_ack);
> +bool check_coredump_req(const struct coredump_req *req, size_t min_size,
> + __u64 required_mask);
> +int open_coredump_tmpfile(int fd_tmpfs_detached);
> +void process_coredump_worker(int fd_coredump, int fd_peer_pidfd, int fd_core_file);
> +
> +#endif /* __COREDUMP_TEST_H */
> diff --git a/tools/testing/selftests/coredump/coredump_test_helpers.c b/tools/testing/selftests/coredump/coredump_test_helpers.c
> new file mode 100644
> index 000000000000..7512a8ef73d3
> --- /dev/null
> +++ b/tools/testing/selftests/coredump/coredump_test_helpers.c
> @@ -0,0 +1,340 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <assert.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <limits.h>
> +#include <linux/coredump.h>
> +#include <linux/fs.h>
> +#include <pthread.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/epoll.h>
> +#include <sys/ioctl.h>
> +#include <sys/socket.h>
> +#include <sys/types.h>
> +#include <sys/un.h>
> +#include <sys/wait.h>
> +#include <unistd.h>
> +
> +#include "../filesystems/wrappers.h"
> +#include "../pidfd/pidfd.h"
> +
> +/* Forward declarations to avoid including harness header */
> +struct __test_metadata;
> +
> +/* Match the fixture definition from coredump_test.h */
> +struct _fixture_coredump_data {
> + char original_core_pattern[256];
> + pid_t pid_coredump_server;
> + int fd_tmpfs_detached;
> +};
> +
> +#ifndef PAGE_SIZE
> +#define PAGE_SIZE 4096
> +#endif
> +
> +#define NUM_THREAD_SPAWN 128
> +
> +void *do_nothing(void *arg)
> +{
> + (void)arg;
> + while (1)
> + pause();
> +
> + return NULL;
> +}
> +
> +void crashing_child(void)
> +{
> + pthread_t thread;
> + int i;
> +
> + for (i = 0; i < NUM_THREAD_SPAWN; ++i)
> + pthread_create(&thread, NULL, do_nothing, NULL);
> +
> + /* crash on purpose */
> + i = *(int *)NULL;
> +}
> +
> +int create_detached_tmpfs(void)
> +{
> + int fd_context, fd_tmpfs;
> +
> + fd_context = sys_fsopen("tmpfs", 0);
> + if (fd_context < 0)
> + return -1;
> +
> + if (sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0)
> + return -1;
> +
> + fd_tmpfs = sys_fsmount(fd_context, 0, 0);
> + close(fd_context);
> + return fd_tmpfs;
> +}
> +
> +int create_and_listen_unix_socket(const char *path)
> +{
> + struct sockaddr_un addr = {
> + .sun_family = AF_UNIX,
> + };
> + assert(strlen(path) < sizeof(addr.sun_path) - 1);
> + strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
> + size_t addr_len =
> + offsetof(struct sockaddr_un, sun_path) + strlen(path) + 1;
> + int fd, ret;
> +
> + fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
> + if (fd < 0)
> + goto out;
> +
> + ret = bind(fd, (const struct sockaddr *)&addr, addr_len);
> + if (ret < 0)
> + goto out;
> +
> + ret = listen(fd, 128);
> + if (ret < 0)
> + goto out;
> +
> + return fd;
> +
> +out:
> + if (fd >= 0)
> + close(fd);
> + return -1;
> +}
> +
> +bool set_core_pattern(const char *pattern)
> +{
> + int fd;
> + ssize_t ret;
> +
> + fd = open("/proc/sys/kernel/core_pattern", O_WRONLY | O_CLOEXEC);
> + if (fd < 0)
> + return false;
> +
> + ret = write(fd, pattern, strlen(pattern));
> + close(fd);
> + if (ret < 0)
> + return false;
> +
> + fprintf(stderr, "Set core_pattern to '%s' | %zu == %zu\n", pattern, ret, strlen(pattern));
> + return ret == strlen(pattern);
> +}
> +
> +int get_peer_pidfd(int fd)
> +{
> + int fd_peer_pidfd;
> + socklen_t fd_peer_pidfd_len = sizeof(fd_peer_pidfd);
> + int ret = getsockopt(fd, SOL_SOCKET, SO_PEERPIDFD, &fd_peer_pidfd,
> + &fd_peer_pidfd_len);
> + if (ret < 0) {
> + fprintf(stderr, "%m - Failed to retrieve peer pidfd for coredump socket connection\n");
> + return -1;
> + }
> + return fd_peer_pidfd;
> +}
> +
> +bool get_pidfd_info(int fd_peer_pidfd, struct pidfd_info *info)
> +{
> + memset(info, 0, sizeof(*info));
> + info->mask = PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP | PIDFD_INFO_COREDUMP_SIGNAL;
> + return ioctl(fd_peer_pidfd, PIDFD_GET_INFO, info) == 0;
> +}
> +
> +/* Protocol helper functions */
> +
> +ssize_t recv_marker(int fd)
> +{
> + enum coredump_mark mark = COREDUMP_MARK_REQACK;
> + ssize_t ret;
> +
> + ret = recv(fd, &mark, sizeof(mark), MSG_WAITALL);
> + if (ret != sizeof(mark))
> + return -1;
> +
> + switch (mark) {
> + case COREDUMP_MARK_REQACK:
> + fprintf(stderr, "Received marker: ReqAck\n");
> + return COREDUMP_MARK_REQACK;
> + case COREDUMP_MARK_MINSIZE:
> + fprintf(stderr, "Received marker: MinSize\n");
> + return COREDUMP_MARK_MINSIZE;
> + case COREDUMP_MARK_MAXSIZE:
> + fprintf(stderr, "Received marker: MaxSize\n");
> + return COREDUMP_MARK_MAXSIZE;
> + case COREDUMP_MARK_UNSUPPORTED:
> + fprintf(stderr, "Received marker: Unsupported\n");
> + return COREDUMP_MARK_UNSUPPORTED;
> + case COREDUMP_MARK_CONFLICTING:
> + fprintf(stderr, "Received marker: Conflicting\n");
> + return COREDUMP_MARK_CONFLICTING;
> + default:
> + fprintf(stderr, "Received unknown marker: %u\n", mark);
> + break;
> + }
> + return -1;
> +}
> +
> +bool read_marker(int fd, enum coredump_mark mark)
> +{
> + ssize_t ret;
> +
> + ret = recv_marker(fd);
> + if (ret < 0)
> + return false;
> + return ret == mark;
> +}
> +
> +bool read_coredump_req(int fd, struct coredump_req *req)
> +{
> + ssize_t ret;
> + size_t field_size, user_size, ack_size, kernel_size, remaining_size;
> +
> + memset(req, 0, sizeof(*req));
> + field_size = sizeof(req->size);
> +
> + /* Peek the size of the coredump request. */
> + ret = recv(fd, req, field_size, MSG_PEEK | MSG_WAITALL);
> + if (ret != field_size)
> + return false;
> + kernel_size = req->size;
> +
> + if (kernel_size < COREDUMP_ACK_SIZE_VER0)
> + return false;
> + if (kernel_size >= PAGE_SIZE)
> + return false;
> +
> + /* Use the minimum of user and kernel size to read the full request. */
> + user_size = sizeof(struct coredump_req);
> + ack_size = user_size < kernel_size ? user_size : kernel_size;
> + ret = recv(fd, req, ack_size, MSG_WAITALL);
> + if (ret != ack_size)
> + return false;
> +
> + fprintf(stderr, "Read coredump request with size %u and mask 0x%llx\n",
> + req->size, (unsigned long long)req->mask);
> +
> + if (user_size > kernel_size)
> + remaining_size = user_size - kernel_size;
> + else
> + remaining_size = kernel_size - user_size;
> +
> + if (PAGE_SIZE <= remaining_size)
> + return false;
> +
> + /*
> + * Discard any additional data if the kernel's request was larger than
> + * what we knew about or cared about.
> + */
> + if (remaining_size) {
> + char buffer[PAGE_SIZE];
> +
> + ret = recv(fd, buffer, sizeof(buffer), MSG_WAITALL);
> + if (ret != remaining_size)
> + return false;
> + fprintf(stderr, "Discarded %zu bytes of data after coredump request\n", remaining_size);
> + }
> +
> + return true;
> +}
> +
> +bool send_coredump_ack(int fd, const struct coredump_req *req,
> + __u64 mask, size_t size_ack)
> +{
> + ssize_t ret;
> + /*
> + * Wrap struct coredump_ack in a larger struct so we can
> + * simulate sending to much data to the kernel.
> + */
> + struct large_ack_for_size_testing {
> + struct coredump_ack ack;
> + char buffer[PAGE_SIZE];
> + } large_ack = {};
> +
> + if (!size_ack)
> + size_ack = sizeof(struct coredump_ack) < req->size_ack ?
> + sizeof(struct coredump_ack) :
> + req->size_ack;
> + large_ack.ack.mask = mask;
> + large_ack.ack.size = size_ack;
> + ret = send(fd, &large_ack, size_ack, MSG_NOSIGNAL);
> + if (ret != size_ack)
> + return false;
> +
> + fprintf(stderr, "Sent coredump ack with size %zu and mask 0x%llx\n",
> + size_ack, (unsigned long long)mask);
> + return true;
> +}
> +
> +bool check_coredump_req(const struct coredump_req *req, size_t min_size,
> + __u64 required_mask)
> +{
> + if (req->size < min_size)
> + return false;
> + if ((req->mask & required_mask) != required_mask)
> + return false;
> + if (req->mask & ~required_mask)
> + return false;
> + return true;
> +}
> +
> +int open_coredump_tmpfile(int fd_tmpfs_detached)
> +{
> + return openat(fd_tmpfs_detached, ".", O_TMPFILE | O_RDWR | O_EXCL, 0600);
> +}
> +
> +void process_coredump_worker(int fd_coredump, int fd_peer_pidfd, int fd_core_file)
> +{
> + int epfd = -1;
> + int exit_code = EXIT_FAILURE;
> + struct epoll_event ev;
> +
> + epfd = epoll_create1(0);
> + if (epfd < 0)
> + goto out;
> +
> + ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
> + ev.data.fd = fd_coredump;
> + if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd_coredump, &ev) < 0)
> + goto out;
> +
> + for (;;) {
> + struct epoll_event events[1];
> + int n = epoll_wait(epfd, events, 1, -1);
> + if (n < 0)
> + break;
> +
> + if (events[0].events & (EPOLLIN | EPOLLRDHUP)) {
> + for (;;) {
> + char buffer[4096];
> + ssize_t bytes_read = read(fd_coredump, buffer, sizeof(buffer));
> + if (bytes_read < 0) {
> + if (errno == EAGAIN || errno == EWOULDBLOCK)
> + break;
> + goto out;
> + }
> + if (bytes_read == 0)
> + goto done;
> + ssize_t bytes_write = write(fd_core_file, buffer, bytes_read);
> + if (bytes_write != bytes_read)
> + goto out;
> + }
> + }
> + }
> +
> +done:
> + exit_code = EXIT_SUCCESS;
> +out:
> + if (epfd >= 0)
> + close(epfd);
> + if (fd_core_file >= 0)
> + close(fd_core_file);
> + if (fd_peer_pidfd >= 0)
> + close(fd_peer_pidfd);
> + if (fd_coredump >= 0)
> + close(fd_coredump);
> + _exit(exit_code);
> +}
>
> --
> 2.47.3
>
Powered by blists - more mailing lists