[PATCH] coredump: add filesz truncation and filter
From: Jacob Lalonde
Date: Fri Jul 31 2026 - 13:24:47 EST
From: Jacob Lalonde <jalalonde@xxxxxx>
This adds opt-in truncation of Coredump segments to
`/proc/pid/coredump_filter`. The primary motivation here is to reduce the
number of zero pages written via pipe to a user space coredump process. In
my testing this reduces coredump size, and thus IO by ~40% at Meta. With
some outliers such as PyTorch's TBE weights being loaded in right before a
crash being closer to 99%.
We achieve this by having the Kernel not emit the trailing zeros of any
VMA, resulting in a PT_LOAD with filesz < memsz. I verified that both GDB
from v16 onward and trunk LLDB support loading cores with truncated
sections, however LLDB won't report zeros for the truncated region
I elected to iterate page by page for simplicity, and because we
short-circuit when encountering the first faulted in page, meaning time
spent iterating should directly replace slower copying and pipe IPC.
Below is an example program where the entire 1 GB VMA would be emitted,
where as with the patch only the first page would be written.
int main(void) {
void *p = mmap(NULL, MMAP_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
int* p2 = (int*)p;
/* write a sentinel to make sure it's not fully sparse */
p2[0] = 0xDEADBEEF;
abort();
}
Signed-off-by: Jacob Lalonde <jalalonde@xxxxxx>
---
fs/coredump.c | 44 +++++++++++++++++++++++++++++++++++++---
include/linux/mm_types.h | 3 ++-
2 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/fs/coredump.c b/fs/coredump.c
index e68a76ff92a3..b6cee412eb99 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -1588,6 +1588,42 @@ static bool always_dump_vma(struct vm_area_struct *vma)
#define DUMP_SIZE_MAYBE_ELFHDR_PLACEHOLDER 1
+/*
+ * Truncate the file_sz of the VMA to the last faulted page
+ */
+static unsigned long truncate_vma(struct vm_area_struct *vma)
+{
+ /*
+ * Same logic as dump_user_range, where we enumerate the pages
+ * in a VMA, but instead of skipping un-faulted pages, we move the VMA
+ * end
+ */
+ struct page *page;
+ unsigned long truncated_end;
+
+ /* We're already under the mmap lock */
+ int locked = 1;
+
+ for (truncated_end = vma->vm_end; truncated_end > vma->vm_start; truncated_end -= PAGE_SIZE) {
+ /*
+ * Because we're iterating backwards, we need to
+ * look at the page before the current address
+ */
+ unsigned long probe_addr = truncated_end - PAGE_SIZE;
+
+ page = get_dump_page(probe_addr, &locked);
+ /*
+ * We hit a faulted page, exit
+ */
+ if (page) {
+ put_page(page);
+ break;
+ }
+ }
+
+ return truncated_end - vma->vm_start;
+}
+
/*
* Decide how much of @vma's contents should be included in a core dump.
*/
@@ -1662,13 +1698,15 @@ static unsigned long vma_dump_size(struct vm_area_struct *vma,
return DUMP_SIZE_MAYBE_ELFHDR_PLACEHOLDER;
}
-#undef FILTER
-
return 0;
whole:
- return vma->vm_end - vma->vm_start;
+ if (FILTER(TRUNCATE_SPARSE_VMAS))
+ return truncate_vma(vma);
+ else
+ return vma->vm_end - vma->vm_start;
}
+#undef FILTER
/*
* Helper function for iterating across a vma list. It ensures that the caller
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index b18c2b2e7d2c..18cf8304f698 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -1932,9 +1932,10 @@ enum {
#define MMF_DUMP_HUGETLB_SHARED 8
#define MMF_DUMP_DAX_PRIVATE 9
#define MMF_DUMP_DAX_SHARED 10
+#define MMF_DUMP_TRUNCATE_SPARSE_VMAS 11
#define MMF_DUMP_FILTER_SHIFT MMF_DUMPABLE_BITS
-#define MMF_DUMP_FILTER_BITS 9
+#define MMF_DUMP_FILTER_BITS 10
#define MMF_DUMP_FILTER_MASK \
((BIT(MMF_DUMP_FILTER_BITS) - 1) << MMF_DUMP_FILTER_SHIFT)
#define MMF_DUMP_FILTER_DEFAULT \
--
2.53.0-Meta