From: Christian Brauner <brauner@kernel.org>
To: linux-fsdevel@vger.kernel.org
Cc: Jacob Lalonde <jalalonde@meta.com>,
Josef Bacik <josef@toxicpanda.com>, Jann Horn <jannh@google.com>,
Alexander Viro <viro@zeniv.linux.org.uk>,
Jan Kara <jack@suse.cz>,
Andrew Morton <akpm@linux-foundation.org>,
David Hildenbrand <david@kernel.org>,
Lorenzo Stoakes <ljs@kernel.org>,
"Liam R. Howlett" <liam@infradead.org>,
Vlastimil Babka <vbabka@kernel.org>,
Mike Rapoport <rppt@kernel.org>,
Suren Baghdasaryan <surenb@google.com>,
Michal Hocko <mhocko@suse.com>,
Omar Sandoval <osandov@osandov.com>,
Jacob Lalonde <jalalonde@fb.com>, Shuah Khan <shuah@kernel.org>,
linux-kernel@vger.kernel.org, linux-mm@kvack.org,
linux-kselftest@vger.kernel.org, linuxppc-dev@lists.ozlabs.org,
"Christian Brauner (Amutable)" <brauner@kernel.org>
Subject: [PATCH 5/6] selftests/coredump: improve coredump size negotiation tests
Date: Fri, 21 Aug 2026 13:52:06 +0200 [thread overview]
Message-ID: <20260821-work-coredump-filter-v1-5-91f9a73ef03e@kernel.org> (raw)
In-Reply-To: <20260821-work-coredump-filter-v1-0-91f9a73ef03e@kernel.org>
Improve the size handling tests when negotiating a coredump through req
and ack.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
.../coredump/coredump_socket_protocol_test.c | 158 +++++++++++++++++++--
.../selftests/coredump/coredump_test_helpers.c | 17 ++-
.../selftests/coredump/coredump_test_helpers.h | 1 +
3 files changed, 155 insertions(+), 21 deletions(-)
diff --git a/tools/testing/selftests/coredump/coredump_socket_protocol_test.c b/tools/testing/selftests/coredump/coredump_socket_protocol_test.c
index 6dcd6c15a565..6c7327832d44 100644
--- a/tools/testing/selftests/coredump/coredump_socket_protocol_test.c
+++ b/tools/testing/selftests/coredump/coredump_socket_protocol_test.c
@@ -1932,11 +1932,74 @@ TEST_F(coredump, socket_request_stream_choice_large)
ASSERT_LT(choice.received, choice.size / 8);
}
+/* What a coredump server was built with. */
+struct server_build {
+ /* sizeof(struct coredump_req) and sizeof(struct coredump_ack) back then. */
+ size_t req_size;
+ size_t ack_size;
+ /* The features it raises if the kernel offers them. */
+ __u64 wants;
+ /* Its policy: what it drops from and adds to the task's selection. */
+ __u64 drop;
+ __u64 add;
+};
+
+/* A server from when the structs were first published: kernel-written dumps. */
+static const struct server_build server_build_ver0 = {
+ .req_size = COREDUMP_REQ_SIZE_VER0,
+ .ack_size = COREDUMP_ACK_SIZE_VER0,
+ .wants = COREDUMP_KERNEL,
+};
+
+/* A server built against this header: no shared memory, always the ELF headers. */
+static const struct server_build server_build_ver1 = {
+ .req_size = sizeof(struct coredump_req),
+ .ack_size = sizeof(struct coredump_ack),
+ .wants = COREDUMP_KERNEL | COREDUMP_RECORDS | COREDUMP_SPARSE |
+ COREDUMP_MEMORY_TYPES,
+ .drop = COREDUMP_MEMORY_ANON_SHARED | COREDUMP_MEMORY_FILE_SHARED,
+ .add = COREDUMP_MEMORY_ELF_HEADERS,
+};
+
+/*
+ * Build the ack the way a server does: from what the kernel offers, what
+ * this build implements, and what fits in the ack the kernel accepts.
+ * Fields the build never read are zero and never consulted.
+ */
+static void negotiate(const struct coredump_req *req,
+ const struct server_build *build,
+ struct coredump_ack *ack)
+{
+ __u64 offered = req->mask & build->wants;
+
+ memset(ack, 0, sizeof(*ack));
+ ack->size = build->ack_size < req->size_ack ? build->ack_size : req->size_ack;
+ /* These builds only ever have the kernel write the coredump. */
+ ack->mask = COREDUMP_KERNEL;
+
+ /* Sparse needs records, records need the kernel to write. */
+ if (offered & COREDUMP_RECORDS) {
+ ack->mask |= COREDUMP_RECORDS;
+ if (offered & COREDUMP_SPARSE)
+ ack->mask |= COREDUMP_SPARSE;
+ }
+
+ /* The memory types need an ack that carries them. */
+ if ((offered & COREDUMP_MEMORY_TYPES) && ack->size >= COREDUMP_ACK_SIZE_VER1) {
+ ack->mask |= COREDUMP_MEMORY_TYPES;
+ /* Start from the task's selection; only advertised types pass. */
+ ack->memory_types = (req->memory_types & ~build->drop) | build->add;
+ ack->memory_types &= req->memory_types_mask;
+ }
+}
+
/* What a memory types test asks of the kernel and what it expects back. */
struct memory_choice {
/* Memory types the crashing child selects, or FILTER_TASK_INHERIT. */
__u64 task_filter;
- /* The ack. */
+ /* Negotiate the ack as this server build, NULL to send it as given. */
+ const struct server_build *build;
+ /* The ack, or what the negotiation must arrive at. */
__u64 mask;
__u64 memory_types;
size_t size_ack;
@@ -1976,6 +2039,13 @@ static void check_memory_dump(struct __test_metadata *const _metadata,
int fd_file = -1;
int exit_code = EXIT_FAILURE;
struct coredump_req req = {};
+ struct coredump_ack ack = {
+ .size = choice->size_ack,
+ .mask = choice->mask,
+ .memory_types = choice->memory_types,
+ };
+ /* How much of the request this server reads. */
+ size_t req_size = choice->build ? choice->build->req_size : sizeof(req);
__u64 task_filter;
ElfW(Phdr) segment;
ssize_t received;
@@ -2006,21 +2076,24 @@ static void check_memory_dump(struct __test_metadata *const _metadata,
if (fd_file < 0)
goto out;
- if (!read_coredump_req(fd_coredump, &req))
- goto out;
-
- if (!check_coredump_req(&req))
+ if (!read_coredump_req_sized(fd_coredump, &req, req_size))
goto out;
- /* The request reports the memory types the task selected. */
if (!peer_coredump_filter(fd_peer_pidfd, &task_filter))
goto out;
- if (req.memory_types != task_filter) {
- fprintf(stderr, "Request reports 0x%llx, task selected 0x%llx\n",
- (unsigned long long)req.memory_types,
- (unsigned long long)task_filter);
- goto out;
+ /* A build from before the memory types never read that far. */
+ if (req_size >= COREDUMP_REQ_SIZE_VER1) {
+ if (!check_coredump_req(&req))
+ goto out;
+
+ /* The request reports the memory types the task selected. */
+ if (req.memory_types != task_filter) {
+ fprintf(stderr, "Request reports 0x%llx, task selected 0x%llx\n",
+ (unsigned long long)req.memory_types,
+ (unsigned long long)task_filter);
+ goto out;
+ }
}
if (choice->task_filter != FILTER_TASK_INHERIT &&
@@ -2035,15 +2108,28 @@ static void check_memory_dump(struct __test_metadata *const _metadata,
if (read_nointr(addr_pipe[0], &addr, sizeof(addr)) != sizeof(addr))
goto out;
- if (!send_coredump_ack_types(fd_coredump, &req, choice->mask,
- choice->memory_types,
- choice->size_ack))
+ /* A server build negotiates its ack and must arrive at the choice. */
+ if (choice->build) {
+ negotiate(&req, choice->build, &ack);
+
+ if (ack.size != choice->size_ack || ack.mask != choice->mask ||
+ ack.memory_types != choice->memory_types) {
+ fprintf(stderr,
+ "Negotiated %u bytes, mask 0x%llx, types 0x%llx\n",
+ ack.size, (unsigned long long)ack.mask,
+ (unsigned long long)ack.memory_types);
+ goto out;
+ }
+ }
+
+ if (!send_coredump_ack_types(fd_coredump, &req, ack.mask,
+ ack.memory_types, ack.size))
goto out;
if (!read_marker(fd_coredump, COREDUMP_MARK_REQACK))
goto out;
- if (choice->mask & COREDUMP_RECORDS)
+ if (ack.mask & COREDUMP_RECORDS)
received = recv_coredump_records(fd_coredump, fd_file,
&size, NULL, -1);
else
@@ -2281,4 +2367,46 @@ TEST_F(coredump, socket_request_memory_types_without_kernel)
check_conflicting_ack(_metadata, self, COREDUMP_USERSPACE | COREDUMP_MEMORY_TYPES);
}
+/*
+ * A server built with the first structs reads the request it knows,
+ * discards the rest and acks with the ack it knows. It raises nothing
+ * it wasn't built for and the kernel dumps what the task selected.
+ */
+TEST_F(coredump, socket_request_negotiate_ver0)
+{
+ struct memory_choice choice = {
+ .task_filter = COREDUMP_MEMORY_ANON_PRIVATE |
+ COREDUMP_MEMORY_ANON_SHARED,
+ .build = &server_build_ver0,
+ .mask = COREDUMP_KERNEL,
+ .memory_types = 0,
+ .size_ack = COREDUMP_ACK_SIZE_VER0,
+ .shared_dumped = true,
+ };
+
+ check_memory_dump(_metadata, self, &choice);
+}
+
+/*
+ * A server built against this header takes every feature the kernel
+ * offers, drops shared memory from what the task selected and adds the
+ * ELF headers.
+ */
+TEST_F(coredump, socket_request_negotiate_ver1)
+{
+ struct memory_choice choice = {
+ .task_filter = COREDUMP_MEMORY_ANON_PRIVATE |
+ COREDUMP_MEMORY_ANON_SHARED,
+ .build = &server_build_ver1,
+ .mask = COREDUMP_KERNEL | COREDUMP_RECORDS | COREDUMP_SPARSE |
+ COREDUMP_MEMORY_TYPES,
+ .memory_types = COREDUMP_MEMORY_ANON_PRIVATE |
+ COREDUMP_MEMORY_ELF_HEADERS,
+ .size_ack = COREDUMP_ACK_SIZE_VER1,
+ .shared_dumped = false,
+ };
+
+ check_memory_dump(_metadata, self, &choice);
+}
+
TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/coredump/coredump_test_helpers.c b/tools/testing/selftests/coredump/coredump_test_helpers.c
index 7ae0c6c458aa..ab94c45cd8be 100644
--- a/tools/testing/selftests/coredump/coredump_test_helpers.c
+++ b/tools/testing/selftests/coredump/coredump_test_helpers.c
@@ -1467,10 +1467,11 @@ bool read_marker(int fd, enum coredump_mark mark)
return ret == mark;
}
-bool read_coredump_req(int fd, struct coredump_req *req)
+/* Read the request as a server built with a @user_size byte struct does. */
+bool read_coredump_req_sized(int fd, struct coredump_req *req, size_t user_size)
{
ssize_t ret;
- size_t field_size, user_size, known_size, kernel_size, remaining_size;
+ size_t field_size, known_size, kernel_size, remaining_size;
memset(req, 0, sizeof(*req));
field_size = sizeof(req->size);
@@ -1478,25 +1479,24 @@ bool read_coredump_req(int fd, struct coredump_req *req)
/* Peek the size of the coredump request. */
ret = recv(fd, req, field_size, MSG_PEEK | MSG_WAITALL);
if (ret != field_size) {
- fprintf(stderr, "read_coredump_req: peek failed (got %zd, expected %zu): %m\n",
+ fprintf(stderr, "%s: peek failed (got %zd, expected %zu): %m\n", __func__,
ret, field_size);
return false;
}
kernel_size = req->size;
if (kernel_size < COREDUMP_REQ_SIZE_VER0) {
- fprintf(stderr, "read_coredump_req: kernel_size %zu < min %d\n",
+ fprintf(stderr, "%s: kernel_size %zu < min %d\n", __func__,
kernel_size, COREDUMP_REQ_SIZE_VER0);
return false;
}
if (kernel_size >= PAGE_SIZE) {
- fprintf(stderr, "read_coredump_req: kernel_size %zu >= PAGE_SIZE %d\n",
+ fprintf(stderr, "%s: kernel_size %zu >= PAGE_SIZE %d\n", __func__,
kernel_size, PAGE_SIZE);
return false;
}
/* Consume as much of the request as we know about. */
- user_size = sizeof(struct coredump_req);
known_size = user_size < kernel_size ? user_size : kernel_size;
ret = recv(fd, req, known_size, MSG_WAITALL);
if (ret != known_size)
@@ -1529,6 +1529,11 @@ bool read_coredump_req(int fd, struct coredump_req *req)
return true;
}
+bool read_coredump_req(int fd, struct coredump_req *req)
+{
+ return read_coredump_req_sized(fd, req, sizeof(*req));
+}
+
/* Send @len bytes of @ack as they are, more than the struct if asked to. */
bool send_coredump_ack_bytes(int fd, const struct coredump_ack *ack, size_t len)
{
diff --git a/tools/testing/selftests/coredump/coredump_test_helpers.h b/tools/testing/selftests/coredump/coredump_test_helpers.h
index fc21b8620359..8e0187645c93 100644
--- a/tools/testing/selftests/coredump/coredump_test_helpers.h
+++ b/tools/testing/selftests/coredump/coredump_test_helpers.h
@@ -65,6 +65,7 @@ bool get_pidfd_info(int fd_peer_pidfd, struct pidfd_info *info);
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 read_coredump_req_sized(int fd, struct coredump_req *req, size_t user_size);
bool send_coredump_ack(int fd, const struct coredump_req *req,
__u64 mask, size_t size_ack);
bool send_coredump_ack_types(int fd, const struct coredump_req *req,
--
2.53.0
next prev parent reply other threads:[~2026-08-21 11:52 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-21 11:52 [PATCH 0/6] coredump: select memory types per request Christian Brauner
2026-08-21 11:52 ` [PATCH 1/6] coredump: select memory types to include Christian Brauner
2026-08-21 11:52 ` [PATCH 2/6] tools: sync coredump.h header Christian Brauner
2026-08-21 11:52 ` [PATCH 3/6] selftests/coredump: simplify the refusal tests Christian Brauner
2026-08-21 11:52 ` [PATCH 4/6] selftests/coredump: test COREDUMP_MEMORY_TYPES Christian Brauner
2026-08-21 11:52 ` Christian Brauner [this message]
2026-08-21 11:52 ` [PATCH 6/6] selftests/coredump: test failed handshakes Christian Brauner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260821-work-coredump-filter-v1-5-91f9a73ef03e@kernel.org \
--to=brauner@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=david@kernel.org \
--cc=jack@suse.cz \
--cc=jalalonde@fb.com \
--cc=jalalonde@meta.com \
--cc=jannh@google.com \
--cc=josef@toxicpanda.com \
--cc=liam@infradead.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=ljs@kernel.org \
--cc=mhocko@suse.com \
--cc=osandov@osandov.com \
--cc=rppt@kernel.org \
--cc=shuah@kernel.org \
--cc=surenb@google.com \
--cc=vbabka@kernel.org \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox