Without objection, I will throw this into rawhide. I want to get some beating on it done before I send it upstream. The related bits that make it cool may hit rawhide soonish. If an F7 update got this too but with dump_dso_headers = 0 default, that would be groovy. It is a no-op when disabled and having the option would be nice for people wanting to play with the userland tools that like this, who don't have a rawhide install. Thanks, Roland --- [PATCH] Add sysctl controls on ELF core dump segments, dump first page of DSOs This adds two sysctl items under fs.binfmt_elf for controlling how memory segments are dumped in ELF core files. The dump_whole_segments option can be set to have private file mappings dumped in their entirety. The dump_dso_headers option is the new default. This dumps the first page (only) of a read-only private file mapping if it appears to be a mapping of an ELF file. Including these pages in the core dump may give sufficient identifying information to associate the original DSO and executable file images and their debugging information with a core file in a generic way just from its contents. Signed-off-by: Roland McGrath <roland@xxxxxxxxxx> --- fs/binfmt_elf.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 128 insertions(+), 24 deletions(-) diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index fa8ea33..ae63521 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -20,6 +20,7 @@ #include <linux/errno.h> #include <linux/signal.h> #include <linux/binfmts.h> +#include <linux/sysctl.h> #include <linux/string.h> #include <linux/file.h> #include <linux/fcntl.h> @@ -1151,6 +1152,10 @@ out: * Modelled on fs/exec.c:aout_core_dump() * Jeremy Fitzhardinge <jeremy@xxxxxxxx> */ + +static int dump_whole_segments = 0; +static int dump_dso_headers = 1; + /* * These are the only things you should do on a core-file: use only these * functions to write out all the necessary info. @@ -1183,31 +1188,60 @@ static int dump_seek(struct file *file, loff_t off) } /* - * Decide whether a segment is worth dumping; default is yes to be - * sure (missing info is worse than too much; etc). - * Personally I'd include everything, and use the coredump limit... - * - * I think we should skip something. But I am not sure how. H.J. + * Decide what to dump of a segment, part, all or none. */ -static int maydump(struct vm_area_struct *vma) +static unsigned long vma_dump_size(struct vm_area_struct *vma) { /* The vma can be set up to tell us the answer directly. */ if (vma->vm_flags & VM_ALWAYSDUMP) - return 1; + goto whole; /* Do not dump I/O mapped devices or special mappings */ if (vma->vm_flags & (VM_IO | VM_RESERVED)) return 0; /* Dump shared memory only if mapped from an anonymous file. */ - if (vma->vm_flags & VM_SHARED) - return vma->vm_file->f_path.dentry->d_inode->i_nlink == 0; - - /* If it hasn't been written to, don't write it out */ - if (!vma->anon_vma) + if (vma->vm_flags & VM_SHARED) { + if (vma->vm_file->f_path.dentry->d_inode->i_nlink == 0) + goto whole; return 0; + } - return 1; + /* Dump segments that have been written to. */ + if (vma->anon_vma) + goto whole; + + if (dump_whole_segments) + goto whole; + + /* + * If this looks like the beginning of a DSO or executable mapping, + * check for an ELF header. If we find one, dump the first page to + * aid in determining what was mapped here. + */ + if (dump_dso_headers && vma->vm_file != NULL && vma->vm_pgoff == 0) { + u32 __user *header = (u32 __user *) vma->vm_start; + u32 word; + /* + * Doing it this way gets the constant folded by GCC. + */ + union { + u32 cmp; + char elfmag[SELFMAG]; + } magic; + BUILD_BUG_ON(SELFMAG != sizeof word); + magic.elfmag[EI_MAG0] = ELFMAG0; + magic.elfmag[EI_MAG1] = ELFMAG1; + magic.elfmag[EI_MAG2] = ELFMAG2; + magic.elfmag[EI_MAG3] = ELFMAG3; + if (get_user(word, header) == 0 && word == magic.cmp) + return PAGE_SIZE; + } + + return 0; + +whole: + return vma->vm_end - vma->vm_start; } /* An ELF note in memory */ @@ -1642,16 +1676,13 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) for (vma = first_vma(current, gate_vma); vma != NULL; vma = next_vma(vma, gate_vma)) { struct elf_phdr phdr; - size_t sz; - - sz = vma->vm_end - vma->vm_start; phdr.p_type = PT_LOAD; phdr.p_offset = offset; phdr.p_vaddr = vma->vm_start; phdr.p_paddr = 0; - phdr.p_filesz = maydump(vma) ? sz : 0; - phdr.p_memsz = sz; + phdr.p_filesz = vma_dump_size(vma); + phdr.p_memsz = vma->vm_end - vma->vm_start; offset += phdr.p_filesz; phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; if (vma->vm_flags & VM_WRITE) @@ -1692,13 +1723,11 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) for (vma = first_vma(current, gate_vma); vma != NULL; vma = next_vma(vma, gate_vma)) { unsigned long addr; + unsigned long end; - if (!maydump(vma)) - continue; + end = vma->vm_start + vma_dump_size(vma); - for (addr = vma->vm_start; - addr < vma->vm_end; - addr += PAGE_SIZE) { + for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) { struct page *page; struct vm_area_struct *vma; @@ -1756,17 +1785,92 @@ cleanup: #undef NUM_NOTES } + +static ctl_table elf_core_dump_sysctls[] = { + { + .ctl_name = CTL_UNNUMBERED, + .procname = "core_dump_whole_segments", + .data = &dump_whole_segments, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "core_dump_dso_headers", + .data = &dump_dso_headers, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { .ctl_name = 0 } +}; + +static ctl_table binfmt_elf_sysctl_dir[] = { + { + .ctl_name = CTL_UNNUMBERED, + .procname = "binfmt_elf", + .mode = 0555, + .child = elf_core_dump_sysctls, + }, + { .ctl_name = 0 } +}; + +static ctl_table binfmt_elf_sysctl_root[] = { + { + .ctl_name = CTL_FS, + .procname = "fs", + .mode = 0555, + .child = binfmt_elf_sysctl_dir, + }, + { .ctl_name = 0 } +}; + +static struct ctl_table_header *core_dump_sysctl_table; + +static int register_core_dump_sysctl(void) +{ + core_dump_sysctl_table = register_sysctl_table(binfmt_elf_sysctl_root); + if (core_dump_sysctl_table == NULL) + return -ENOMEM; + return 0; +} + +static void unregister_core_dump_sysctl(void) +{ + unregister_sysctl_table(core_dump_sysctl_table); + core_dump_sysctl_table = NULL; +} + +#else + +static int register_core_dump_sysctl(void) +{ + return 0; +} + +static void unregister_core_dump_sysctl(void) +{ +} + #endif /* USE_ELF_CORE_DUMP */ static int __init init_elf_binfmt(void) { - return register_binfmt(&elf_format); + int error = register_core_dump_sysctl(); + if (!error) { + error = register_binfmt(&elf_format); + if (error) + unregister_core_dump_sysctl(); + } + return error; } static void __exit exit_elf_binfmt(void) { /* Remove the COFF and ELF loaders. */ unregister_binfmt(&elf_format); + unregister_core_dump_sysctl(); } core_initcall(init_elf_binfmt); -- 1.5.2.1 _______________________________________________ Fedora-kernel-list mailing list Fedora-kernel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/fedora-kernel-list