aboutsummaryrefslogtreecommitdiffstats
path: root/fs/binfmt_elf_fdpic.c
diff options
context:
space:
mode:
authorDaisuke HATAYAMA <d.hatayama@jp.fujitsu.com>2010-03-05 16:44:05 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2010-03-06 14:26:45 -0500
commit05f47fda9fc5b17bfab189e9d54228025befc996 (patch)
treef42e4f13eed3d52022a629e6697c96f6155c9d66 /fs/binfmt_elf_fdpic.c
parent12bac0d9f4dbf3445a0319beee848d15fa32775e (diff)
coredump: unify dump_seek() implementations for each binfmt_*.c
The current ELF dumper can produce broken corefiles if program headers exceed 65535. In particular, the program in 64-bit environment often demands more than 65535 mmaps. If you google max_map_count, then you can find many users facing this problem. Solaris has already dealt with this issue, and other OSes have also adopted the same method as in Solaris. Currently, Sun's document and AMD 64 ABI include the description for the extension, where they call the extension Extended Numbering. See Reference for further information. I believe that linux kernel should adopt the same way as they did, so I've written this patch. I am also preparing for patches of GDB and binutils. How to fix ========== In new dumping process, there are two cases according to weather or not the number of program headers is equal to or more than 65535. - if less than 65535, the produced corefile format is exactly the same as the ordinary one. - if equal to or more than 65535, then e_phnum field is set to newly introduced constant PN_XNUM(0xffff) and the actual number of program headers is set to sh_info field of the section header at index 0. Compatibility Concern ===================== * As already mentioned in Summary, Sun and AMD64 has already adopted this. See Reference. * There are four combinations according to whether kernel and userland tools are respectively modified or not. The next table summarizes shortly for each combination. --------------------------------------------- Original Kernel | Modified Kernel --------------------------------------------- < 65535 | >= 65535 | < 65535 | >= 65535 ------------------------------------------------------------- Original Tools | OK | broken | OK | broken (#) ------------------------------------------------------------- Modified Tools | OK | broken | OK | OK ------------------------------------------------------------- Note that there is no case that `OK' changes to `broken'. (#) Although this case remains broken, O-M behaves better than O-O. That is, while in O-O case e_phnum field would be extremely small due to integer overflow, in O-M case it is guaranteed to be at least 65535 by being set to PN_XNUM(0xFFFF), much closer to the actual correct value than the O-O case. Test Program ============ Here is a test program mkmmaps.c that is useful to produce the corefile with many mmaps. To use this, please take the following steps: $ ulimit -c unlimited $ sysctl vm.max_map_count=70000 # default 65530 is too small $ sysctl fs.file-max=70000 $ mkmmaps 65535 Then, the program will abort and a corefile will be generated. If failed, there are two cases according to the error message displayed. * ``out of memory'' means vm.max_map_count is still smaller * ``too many open files'' means fs.file-max is still smaller So, please change it to a larger value, and then retry it. mkmmaps.c == #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> int main(int argc, char **argv) { int maps_num; if (argc < 2) { fprintf(stderr, "mkmmaps [number of maps to be created]\n"); exit(1); } if (sscanf(argv[1], "%d", &maps_num) == EOF) { perror("sscanf"); exit(2); } if (maps_num < 0) { fprintf(stderr, "%d is invalid\n", maps_num); exit(3); } for (; maps_num > 0; --maps_num) { if (MAP_FAILED == mmap((void *)NULL, (size_t) 1, PROT_READ, MAP_SHARED | MAP_ANONYMOUS, (int) -1, (off_t) NULL)) { perror("mmap"); exit(4); } } abort(); { char buffer[128]; sprintf(buffer, "wc -l /proc/%u/maps", getpid()); system(buffer); } return 0; } Tested on i386, ia64 and um/sys-i386. Built on sh4 (which covers fs/binfmt_elf_fdpic.c) References ========== - Sun microsystems: Linker and Libraries. Part No: 817-1984-17, September 2008. URL: http://docs.sun.com/app/docs/doc/817-1984 - System V ABI AMD64 Architecture Processor Supplement Draft Version 0.99., May 11, 2009. URL: http://www.x86-64.org/ This patch: There are three different definitions for dump_seek() functions in binfmt_aout.c, binfmt_elf.c and binfmt_elf_fdpic.c, respectively. The only for binfmt_elf.c. My next patch will move dump_seek() into a header file in order to share the same implementations for dump_write() and dump_seek(). As the first step, this patch unify these three definitions for dump_seek() by applying the past commits that have been applied only for binfmt_elf.c. Specifically, the modification made here is part of the following commits: * d025c9db7f31fc0554ce7fb2dfc78d35a77f3487 * 7f14daa19ea36b200d237ad3ac5826ae25360461 This patch does not change a shape of corefiles. Signed-off-by: Daisuke HATAYAMA <d.hatayama@jp.fujitsu.com> Cc: "Luck, Tony" <tony.luck@intel.com> Cc: Jeff Dike <jdike@addtoit.com> Cc: David Howells <dhowells@redhat.com> Cc: Greg Ungerer <gerg@snapgear.com> Cc: Roland McGrath <roland@redhat.com> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: Andi Kleen <andi@firstfloor.org> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Cc: <linux-arch@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/binfmt_elf_fdpic.c')
-rw-r--r--fs/binfmt_elf_fdpic.c59
1 files changed, 38 insertions, 21 deletions
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index 18d77297ccc8..32d9b44c3cb9 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -1226,11 +1226,22 @@ static int dump_write(struct file *file, const void *addr, int nr)
1226 1226
1227static int dump_seek(struct file *file, loff_t off) 1227static int dump_seek(struct file *file, loff_t off)
1228{ 1228{
1229 if (file->f_op->llseek) { 1229 if (file->f_op->llseek && file->f_op->llseek != no_llseek) {
1230 if (file->f_op->llseek(file, off, SEEK_SET) != off) 1230 if (file->f_op->llseek(file, off, SEEK_CUR) < 0)
1231 return 0; 1231 return 0;
1232 } else { 1232 } else {
1233 file->f_pos = off; 1233 char *buf = (char *)get_zeroed_page(GFP_KERNEL);
1234 if (!buf)
1235 return 0;
1236 while (off > 0) {
1237 unsigned long n = off;
1238 if (n > PAGE_SIZE)
1239 n = PAGE_SIZE;
1240 if (!dump_write(file, buf, n))
1241 return 0;
1242 off -= n;
1243 }
1244 free_page((unsigned long)buf);
1234 } 1245 }
1235 return 1; 1246 return 1;
1236} 1247}
@@ -1313,30 +1324,35 @@ static int notesize(struct memelfnote *en)
1313 1324
1314/* #define DEBUG */ 1325/* #define DEBUG */
1315 1326
1316#define DUMP_WRITE(addr, nr) \ 1327#define DUMP_WRITE(addr, nr, foffset) \
1317 do { if (!dump_write(file, (addr), (nr))) return 0; } while(0) 1328 do { if (!dump_write(file, (addr), (nr))) return 0; *foffset += (nr); } while(0)
1318#define DUMP_SEEK(off) \
1319 do { if (!dump_seek(file, (off))) return 0; } while(0)
1320 1329
1321static int writenote(struct memelfnote *men, struct file *file) 1330static int alignfile(struct file *file, loff_t *foffset)
1322{ 1331{
1323 struct elf_note en; 1332 static const char buf[4] = { 0, };
1333 DUMP_WRITE(buf, roundup(*foffset, 4) - *foffset, foffset);
1334 return 1;
1335}
1324 1336
1337static int writenote(struct memelfnote *men, struct file *file,
1338 loff_t *foffset)
1339{
1340 struct elf_note en;
1325 en.n_namesz = strlen(men->name) + 1; 1341 en.n_namesz = strlen(men->name) + 1;
1326 en.n_descsz = men->datasz; 1342 en.n_descsz = men->datasz;
1327 en.n_type = men->type; 1343 en.n_type = men->type;
1328 1344
1329 DUMP_WRITE(&en, sizeof(en)); 1345 DUMP_WRITE(&en, sizeof(en), foffset);
1330 DUMP_WRITE(men->name, en.n_namesz); 1346 DUMP_WRITE(men->name, en.n_namesz, foffset);
1331 /* XXX - cast from long long to long to avoid need for libgcc.a */ 1347 if (!alignfile(file, foffset))
1332 DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */ 1348 return 0;
1333 DUMP_WRITE(men->data, men->datasz); 1349 DUMP_WRITE(men->data, men->datasz, foffset);
1334 DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */ 1350 if (!alignfile(file, foffset))
1351 return 0;
1335 1352
1336 return 1; 1353 return 1;
1337} 1354}
1338#undef DUMP_WRITE 1355#undef DUMP_WRITE
1339#undef DUMP_SEEK
1340 1356
1341#define DUMP_WRITE(addr, nr) \ 1357#define DUMP_WRITE(addr, nr) \
1342 if ((size += (nr)) > cprm->limit || \ 1358 if ((size += (nr)) > cprm->limit || \
@@ -1552,7 +1568,7 @@ static int elf_fdpic_dump_segments(struct file *file, size_t *size,
1552 err = -EIO; 1568 err = -EIO;
1553 kunmap(page); 1569 kunmap(page);
1554 page_cache_release(page); 1570 page_cache_release(page);
1555 } else if (!dump_seek(file, file->f_pos + PAGE_SIZE)) 1571 } else if (!dump_seek(file, PAGE_SIZE))
1556 err = -EFBIG; 1572 err = -EFBIG;
1557 if (err) 1573 if (err)
1558 goto out; 1574 goto out;
@@ -1605,7 +1621,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
1605 int i; 1621 int i;
1606 struct vm_area_struct *vma; 1622 struct vm_area_struct *vma;
1607 struct elfhdr *elf = NULL; 1623 struct elfhdr *elf = NULL;
1608 loff_t offset = 0, dataoff; 1624 loff_t offset = 0, dataoff, foffset;
1609 int numnote; 1625 int numnote;
1610 struct memelfnote *notes = NULL; 1626 struct memelfnote *notes = NULL;
1611 struct elf_prstatus *prstatus = NULL; /* NT_PRSTATUS */ 1627 struct elf_prstatus *prstatus = NULL; /* NT_PRSTATUS */
@@ -1730,6 +1746,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
1730 DUMP_WRITE(elf, sizeof(*elf)); 1746 DUMP_WRITE(elf, sizeof(*elf));
1731 offset += sizeof(*elf); /* Elf header */ 1747 offset += sizeof(*elf); /* Elf header */
1732 offset += (segs+1) * sizeof(struct elf_phdr); /* Program headers */ 1748 offset += (segs+1) * sizeof(struct elf_phdr); /* Program headers */
1749 foffset = offset;
1733 1750
1734 /* Write notes phdr entry */ 1751 /* Write notes phdr entry */
1735 { 1752 {
@@ -1786,7 +1803,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
1786 1803
1787 /* write out the notes section */ 1804 /* write out the notes section */
1788 for (i = 0; i < numnote; i++) 1805 for (i = 0; i < numnote; i++)
1789 if (!writenote(notes + i, cprm->file)) 1806 if (!writenote(notes + i, cprm->file, &foffset))
1790 goto end_coredump; 1807 goto end_coredump;
1791 1808
1792 /* write out the thread status notes section */ 1809 /* write out the thread status notes section */
@@ -1795,11 +1812,11 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
1795 list_entry(t, struct elf_thread_status, list); 1812 list_entry(t, struct elf_thread_status, list);
1796 1813
1797 for (i = 0; i < tmp->num_notes; i++) 1814 for (i = 0; i < tmp->num_notes; i++)
1798 if (!writenote(&tmp->notes[i], cprm->file)) 1815 if (!writenote(&tmp->notes[i], cprm->file, &foffset))
1799 goto end_coredump; 1816 goto end_coredump;
1800 } 1817 }
1801 1818
1802 if (!dump_seek(cprm->file, dataoff)) 1819 if (!dump_seek(cprm->file, dataoff - foffset))
1803 goto end_coredump; 1820 goto end_coredump;
1804 1821
1805 if (elf_fdpic_dump_segments(cprm->file, &size, &cprm->limit, 1822 if (elf_fdpic_dump_segments(cprm->file, &size, &cprm->limit,