]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
coredump: Also dump first pages of non-executable ELF libraries
authorJann Horn <jannh@google.com>
Wed, 26 Jan 2022 02:57:39 +0000 (03:57 +0100)
committerStefan Bader <stefan.bader@canonical.com>
Wed, 27 Apr 2022 09:56:41 +0000 (11:56 +0200)
BugLink: https://bugs.launchpad.net/bugs/1969110
commit 84158b7f6a0624b81800b4e7c90f7fb7fdecf66c upstream.

When I rewrote the VMA dumping logic for coredumps, I changed it to
recognize ELF library mappings based on the file being executable instead
of the mapping having an ELF header. But turns out, distros ship many ELF
libraries as non-executable, so the heuristic goes wrong...

Restore the old behavior where FILTER(ELF_HEADERS) dumps the first page of
any offset-0 readable mapping that starts with the ELF magic.

This fix is technically layer-breaking a bit, because it checks for
something ELF-specific in fs/coredump.c; but since we probably want to
share this between standard ELF and FDPIC ELF anyway, I guess it's fine?
And this also keeps the change small for backporting.

Cc: stable@vger.kernel.org
Fixes: 429a22e776a2 ("coredump: rework elf/elf_fdpic vma_dump_size() into common helper")
Reported-by: Bill Messmer <wmessmer@microsoft.com>
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20220126025739.2014888-1-jannh@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
(cherry picked from commit 6cdb84dd0c8dd030f37a710f21bf69abc7229001)
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
fs/coredump.c

index 3224dee44d30ed3206ec3881545a540d6c59db56..bb66a4e3819f16795fb2f24e467fd71b8300d55e 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/fs.h>
 #include <linux/path.h>
 #include <linux/timekeeping.h>
+#include <linux/elf.h>
 
 #include <linux/uaccess.h>
 #include <asm/mmu_context.h>
@@ -992,6 +993,8 @@ static bool always_dump_vma(struct vm_area_struct *vma)
        return false;
 }
 
+#define DUMP_SIZE_MAYBE_ELFHDR_PLACEHOLDER 1
+
 /*
  * Decide how much of @vma's contents should be included in a core dump.
  */
@@ -1051,9 +1054,20 @@ static unsigned long vma_dump_size(struct vm_area_struct *vma,
         * dump the first page to aid in determining what was mapped here.
         */
        if (FILTER(ELF_HEADERS) &&
-           vma->vm_pgoff == 0 && (vma->vm_flags & VM_READ) &&
-           (READ_ONCE(file_inode(vma->vm_file)->i_mode) & 0111) != 0)
-               return PAGE_SIZE;
+           vma->vm_pgoff == 0 && (vma->vm_flags & VM_READ)) {
+               if ((READ_ONCE(file_inode(vma->vm_file)->i_mode) & 0111) != 0)
+                       return PAGE_SIZE;
+
+               /*
+                * ELF libraries aren't always executable.
+                * We'll want to check whether the mapping starts with the ELF
+                * magic, but not now - we're holding the mmap lock,
+                * so copy_from_user() doesn't work here.
+                * Use a placeholder instead, and fix it up later in
+                * dump_vma_snapshot().
+                */
+               return DUMP_SIZE_MAYBE_ELFHDR_PLACEHOLDER;
+       }
 
 #undef FILTER
 
@@ -1128,8 +1142,6 @@ int dump_vma_snapshot(struct coredump_params *cprm, int *vma_count,
                m->end = vma->vm_end;
                m->flags = vma->vm_flags;
                m->dump_size = vma_dump_size(vma, cprm->mm_flags);
-
-               vma_data_size += m->dump_size;
        }
 
        mmap_write_unlock(mm);
@@ -1139,6 +1151,23 @@ int dump_vma_snapshot(struct coredump_params *cprm, int *vma_count,
                return -EFAULT;
        }
 
+       for (i = 0; i < *vma_count; i++) {
+               struct core_vma_metadata *m = (*vma_meta) + i;
+
+               if (m->dump_size == DUMP_SIZE_MAYBE_ELFHDR_PLACEHOLDER) {
+                       char elfmag[SELFMAG];
+
+                       if (copy_from_user(elfmag, (void __user *)m->start, SELFMAG) ||
+                                       memcmp(elfmag, ELFMAG, SELFMAG) != 0) {
+                               m->dump_size = 0;
+                       } else {
+                               m->dump_size = PAGE_SIZE;
+                       }
+               }
+
+               vma_data_size += m->dump_size;
+       }
+
        *vma_data_size_ptr = vma_data_size;
        return 0;
 }