[PATCH v2 11/22] coredump: always chunk writes
From: Christian Brauner
Date: Wed Aug 19 2026 - 19:11:44 EST
Right now dump_emit() is the only coredump helper that writes buffers
larger than a page in one call. For elf notes that can easily blow past
PAGE_SIZE. That's annoying because neither pipes nor af_unix sockets
take such writes in one piece.
If a signal arrives while the writer is waiting they drop a short write.
With the coredump records work coming up that means header and its data
are desynchronized. A write that fits in one pipe buffer or one skb
doesn't suffer from this.
So split all writes up, including elf notes, and cap every write at a
page. The coredump socket already raises sk_sndbuf far enough for a page
to fit a single skb and pipes always work that way.
That means dump_interrupted() is now checked once per page. So a large
coredump stops earlier (good). An empty write no longer issues a
zero-length write. The rlimit core check stays where it was. It
continues refusing whole writes.
Signed-off-by: Christian Brauner (Amutable) <brauner@xxxxxxxxxx>
---
fs/coredump.c | 37 ++++++++++++++++++++++++++++++-------
1 file changed, 30 insertions(+), 7 deletions(-)
diff --git a/fs/coredump.c b/fs/coredump.c
index d837819031ff..d61f36239f91 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -1215,19 +1215,21 @@ void vfs_coredump(const kernel_siginfo_t *siginfo)
* do on a core-file: use only these functions to write out all the
* necessary info.
*/
-static bool __dump_emit(struct coredump_params *cprm, const void *addr, int nr)
+/* One write, never more than a page. See __dump_emit(). */
+static bool dump_emit_chunk(struct coredump_params *cprm, const void *addr,
+ int nr)
{
struct file *file = cprm->file;
loff_t pos = file->f_pos;
ssize_t n;
- if (cprm->written + nr > cprm->limit)
- return false;
if (dump_interrupted())
return false;
+
n = __kernel_write(file, addr, nr, &pos);
if (n != nr)
return false;
+
file->f_pos = pos;
cprm->written += n;
cprm->pos += n;
@@ -1235,6 +1237,24 @@ static bool __dump_emit(struct coredump_params *cprm, const void *addr, int nr)
return true;
}
+static bool __dump_emit(struct coredump_params *cprm, const void *addr, int nr)
+{
+ if (cprm->written + nr > cprm->limit)
+ return false;
+
+ while (nr) {
+ int chunk = min_t(int, nr, PAGE_SIZE);
+
+ if (!dump_emit_chunk(cprm, addr, chunk))
+ return false;
+
+ addr += chunk;
+ nr -= chunk;
+ }
+
+ return true;
+}
+
static bool __dump_skip(struct coredump_params *cprm, size_t nr)
{
static char zeroes[PAGE_SIZE];
@@ -1247,13 +1267,16 @@ static bool __dump_skip(struct coredump_params *cprm, size_t nr)
return true;
}
- while (nr > PAGE_SIZE) {
- if (!__dump_emit(cprm, zeroes, PAGE_SIZE))
+ while (nr) {
+ size_t chunk = min_t(size_t, nr, PAGE_SIZE);
+
+ if (!__dump_emit(cprm, zeroes, chunk))
return false;
- nr -= PAGE_SIZE;
+
+ nr -= chunk;
}
- return __dump_emit(cprm, zeroes, nr);
+ return true;
}
/* Flush the accumulated hole before writing data. */
--
2.53.0