linux-2.6-elf-core-sysctl.patch

Roland McGrath roland at redhat.com
Fri Jun 29 02:02:41 UTC 2007


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 at redhat.com>
---
 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 at sw.oz.au>
  */
+
+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




More information about the Fedora-kernel-list mailing list