aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorDaisuke HATAYAMA <d.hatayama@jp.fujitsu.com>2010-03-05 16:44:10 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2010-03-06 14:26:46 -0500
commit8d9032bbe4671dc481261ccd4e161cd96e54b118 (patch)
treea31d22f488f7d6789259da68c53cb2727a925fa8 /fs
parent93eb211e6c9ff6054fcf9c5b9e344d8d9ad29175 (diff)
elf coredump: add extended numbering support
The current ELF dumper implementation can produce broken corefiles if program headers exceed 65535. This number is determined by the number of vmas which the process have. In particular, some extreme programs may use more than 65535 vmas. (If you google max_map_count, you can find some users facing this problem.) This kind of program never be able to generate correct coredumps. This patch implements ``extended numbering'' that uses sh_info field of the first section header instead of e_phnum field in order to represent upto 4294967295 vmas. This is supported by AMD64-ABI(http://www.x86-64.org/documentation.html) and Solaris(http://docs.sun.com/app/docs/doc/817-1984/). Of course, we are preparing patches for gdb and binutils. 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')
-rw-r--r--fs/binfmt_elf.c66
-rw-r--r--fs/binfmt_elf_fdpic.c63
2 files changed, 124 insertions, 5 deletions
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 6fc49b6ed936..78de530cfb02 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1838,6 +1838,34 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma,
1838 return gate_vma; 1838 return gate_vma;
1839} 1839}
1840 1840
1841static void fill_extnum_info(struct elfhdr *elf, struct elf_shdr *shdr4extnum,
1842 elf_addr_t e_shoff, int segs)
1843{
1844 elf->e_shoff = e_shoff;
1845 elf->e_shentsize = sizeof(*shdr4extnum);
1846 elf->e_shnum = 1;
1847 elf->e_shstrndx = SHN_UNDEF;
1848
1849 memset(shdr4extnum, 0, sizeof(*shdr4extnum));
1850
1851 shdr4extnum->sh_type = SHT_NULL;
1852 shdr4extnum->sh_size = elf->e_shnum;
1853 shdr4extnum->sh_link = elf->e_shstrndx;
1854 shdr4extnum->sh_info = segs;
1855}
1856
1857static size_t elf_core_vma_data_size(struct vm_area_struct *gate_vma,
1858 unsigned long mm_flags)
1859{
1860 struct vm_area_struct *vma;
1861 size_t size = 0;
1862
1863 for (vma = first_vma(current, gate_vma); vma != NULL;
1864 vma = next_vma(vma, gate_vma))
1865 size += vma_dump_size(vma, mm_flags);
1866 return size;
1867}
1868
1841/* 1869/*
1842 * Actual dumper 1870 * Actual dumper
1843 * 1871 *
@@ -1857,6 +1885,9 @@ static int elf_core_dump(struct coredump_params *cprm)
1857 unsigned long mm_flags; 1885 unsigned long mm_flags;
1858 struct elf_note_info info; 1886 struct elf_note_info info;
1859 struct elf_phdr *phdr4note = NULL; 1887 struct elf_phdr *phdr4note = NULL;
1888 struct elf_shdr *shdr4extnum = NULL;
1889 Elf_Half e_phnum;
1890 elf_addr_t e_shoff;
1860 1891
1861 /* 1892 /*
1862 * We no longer stop all VM operations. 1893 * We no longer stop all VM operations.
@@ -1885,12 +1916,19 @@ static int elf_core_dump(struct coredump_params *cprm)
1885 if (gate_vma != NULL) 1916 if (gate_vma != NULL)
1886 segs++; 1917 segs++;
1887 1918
1919 /* for notes section */
1920 segs++;
1921
1922 /* If segs > PN_XNUM(0xffff), then e_phnum overflows. To avoid
1923 * this, kernel supports extended numbering. Have a look at
1924 * include/linux/elf.h for further information. */
1925 e_phnum = segs > PN_XNUM ? PN_XNUM : segs;
1926
1888 /* 1927 /*
1889 * Collect all the non-memory information about the process for the 1928 * Collect all the non-memory information about the process for the
1890 * notes. This also sets up the file header. 1929 * notes. This also sets up the file header.
1891 */ 1930 */
1892 if (!fill_note_info(elf, segs + 1, /* including notes section */ 1931 if (!fill_note_info(elf, e_phnum, &info, cprm->signr, cprm->regs))
1893 &info, cprm->signr, cprm->regs))
1894 goto cleanup; 1932 goto cleanup;
1895 1933
1896 has_dumped = 1; 1934 has_dumped = 1;
@@ -1900,7 +1938,7 @@ static int elf_core_dump(struct coredump_params *cprm)
1900 set_fs(KERNEL_DS); 1938 set_fs(KERNEL_DS);
1901 1939
1902 offset += sizeof(*elf); /* Elf header */ 1940 offset += sizeof(*elf); /* Elf header */
1903 offset += (segs + 1) * sizeof(struct elf_phdr); /* Program headers */ 1941 offset += segs * sizeof(struct elf_phdr); /* Program headers */
1904 foffset = offset; 1942 foffset = offset;
1905 1943
1906 /* Write notes phdr entry */ 1944 /* Write notes phdr entry */
@@ -1926,6 +1964,19 @@ static int elf_core_dump(struct coredump_params *cprm)
1926 */ 1964 */
1927 mm_flags = current->mm->flags; 1965 mm_flags = current->mm->flags;
1928 1966
1967 offset += elf_core_vma_data_size(gate_vma, mm_flags);
1968 offset += elf_core_extra_data_size();
1969 e_shoff = offset;
1970
1971 if (e_phnum == PN_XNUM) {
1972 shdr4extnum = kmalloc(sizeof(*shdr4extnum), GFP_KERNEL);
1973 if (!shdr4extnum)
1974 goto end_coredump;
1975 fill_extnum_info(elf, shdr4extnum, e_shoff, segs);
1976 }
1977
1978 offset = dataoff;
1979
1929 size += sizeof(*elf); 1980 size += sizeof(*elf);
1930 if (size > cprm->limit || !dump_write(cprm->file, elf, sizeof(*elf))) 1981 if (size > cprm->limit || !dump_write(cprm->file, elf, sizeof(*elf)))
1931 goto end_coredump; 1982 goto end_coredump;
@@ -2003,11 +2054,20 @@ static int elf_core_dump(struct coredump_params *cprm)
2003 if (!elf_core_write_extra_data(cprm->file, &size, cprm->limit)) 2054 if (!elf_core_write_extra_data(cprm->file, &size, cprm->limit))
2004 goto end_coredump; 2055 goto end_coredump;
2005 2056
2057 if (e_phnum == PN_XNUM) {
2058 size += sizeof(*shdr4extnum);
2059 if (size > cprm->limit
2060 || !dump_write(cprm->file, shdr4extnum,
2061 sizeof(*shdr4extnum)))
2062 goto end_coredump;
2063 }
2064
2006end_coredump: 2065end_coredump:
2007 set_fs(fs); 2066 set_fs(fs);
2008 2067
2009cleanup: 2068cleanup:
2010 free_note_info(&info); 2069 free_note_info(&info);
2070 kfree(shdr4extnum);
2011 kfree(phdr4note); 2071 kfree(phdr4note);
2012 kfree(elf); 2072 kfree(elf);
2013out: 2073out:
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index 112da491d75d..e49d9c06a4b6 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -1505,6 +1505,22 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t)
1505 return sz; 1505 return sz;
1506} 1506}
1507 1507
1508static void fill_extnum_info(struct elfhdr *elf, struct elf_shdr *shdr4extnum,
1509 elf_addr_t e_shoff, int segs)
1510{
1511 elf->e_shoff = e_shoff;
1512 elf->e_shentsize = sizeof(*shdr4extnum);
1513 elf->e_shnum = 1;
1514 elf->e_shstrndx = SHN_UNDEF;
1515
1516 memset(shdr4extnum, 0, sizeof(*shdr4extnum));
1517
1518 shdr4extnum->sh_type = SHT_NULL;
1519 shdr4extnum->sh_size = elf->e_shnum;
1520 shdr4extnum->sh_link = elf->e_shstrndx;
1521 shdr4extnum->sh_info = segs;
1522}
1523
1508/* 1524/*
1509 * dump the segments for an MMU process 1525 * dump the segments for an MMU process
1510 */ 1526 */
@@ -1569,6 +1585,17 @@ static int elf_fdpic_dump_segments(struct file *file, size_t *size,
1569} 1585}
1570#endif 1586#endif
1571 1587
1588static size_t elf_core_vma_data_size(unsigned long mm_flags)
1589{
1590 struct vm_area_struct *vma;
1591 size_t size = 0;
1592
1593 for (vma = current->mm->mmap; vma; vma->vm_next)
1594 if (maydump(vma, mm_flags))
1595 size += vma->vm_end - vma->vm_start;
1596 return size;
1597}
1598
1572/* 1599/*
1573 * Actual dumper 1600 * Actual dumper
1574 * 1601 *
@@ -1601,6 +1628,9 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
1601 elf_addr_t *auxv; 1628 elf_addr_t *auxv;
1602 unsigned long mm_flags; 1629 unsigned long mm_flags;
1603 struct elf_phdr *phdr4note = NULL; 1630 struct elf_phdr *phdr4note = NULL;
1631 struct elf_shdr *shdr4extnum = NULL;
1632 Elf_Half e_phnum;
1633 elf_addr_t e_shoff;
1604 1634
1605 /* 1635 /*
1606 * We no longer stop all VM operations. 1636 * We no longer stop all VM operations.
@@ -1667,8 +1697,16 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
1667 segs = current->mm->map_count; 1697 segs = current->mm->map_count;
1668 segs += elf_core_extra_phdrs(); 1698 segs += elf_core_extra_phdrs();
1669 1699
1700 /* for notes section */
1701 segs++;
1702
1703 /* If segs > PN_XNUM(0xffff), then e_phnum overflows. To avoid
1704 * this, kernel supports extended numbering. Have a look at
1705 * include/linux/elf.h for further information. */
1706 e_phnum = segs > PN_XNUM ? PN_XNUM : segs;
1707
1670 /* Set up header */ 1708 /* Set up header */
1671 fill_elf_fdpic_header(elf, segs + 1); /* including notes section */ 1709 fill_elf_fdpic_header(elf, e_phnum);
1672 1710
1673 has_dumped = 1; 1711 has_dumped = 1;
1674 current->flags |= PF_DUMPCORE; 1712 current->flags |= PF_DUMPCORE;
@@ -1708,7 +1746,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
1708 set_fs(KERNEL_DS); 1746 set_fs(KERNEL_DS);
1709 1747
1710 offset += sizeof(*elf); /* Elf header */ 1748 offset += sizeof(*elf); /* Elf header */
1711 offset += (segs+1) * sizeof(struct elf_phdr); /* Program headers */ 1749 offset += segs * sizeof(struct elf_phdr); /* Program headers */
1712 foffset = offset; 1750 foffset = offset;
1713 1751
1714 /* Write notes phdr entry */ 1752 /* Write notes phdr entry */
@@ -1738,6 +1776,19 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
1738 */ 1776 */
1739 mm_flags = current->mm->flags; 1777 mm_flags = current->mm->flags;
1740 1778
1779 offset += elf_core_vma_data_size(mm_flags);
1780 offset += elf_core_extra_data_size();
1781 e_shoff = offset;
1782
1783 if (e_phnum == PN_XNUM) {
1784 shdr4extnum = kmalloc(sizeof(*shdr4extnum), GFP_KERNEL);
1785 if (!shdr4extnum)
1786 goto end_coredump;
1787 fill_extnum_info(elf, shdr4extnum, e_shoff, segs);
1788 }
1789
1790 offset = dataoff;
1791
1741 size += sizeof(*elf); 1792 size += sizeof(*elf);
1742 if (size > cprm->limit || !dump_write(cprm->file, elf, sizeof(*elf))) 1793 if (size > cprm->limit || !dump_write(cprm->file, elf, sizeof(*elf)))
1743 goto end_coredump; 1794 goto end_coredump;
@@ -1802,6 +1853,14 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
1802 if (!elf_core_write_extra_data(cprm->file, &size, cprm->limit)) 1853 if (!elf_core_write_extra_data(cprm->file, &size, cprm->limit))
1803 goto end_coredump; 1854 goto end_coredump;
1804 1855
1856 if (e_phnum == PN_XNUM) {
1857 size += sizeof(*shdr4extnum);
1858 if (size > cprm->limit
1859 || !dump_write(cprm->file, shdr4extnum,
1860 sizeof(*shdr4extnum)))
1861 goto end_coredump;
1862 }
1863
1805 if (cprm->file->f_pos != offset) { 1864 if (cprm->file->f_pos != offset) {
1806 /* Sanity check */ 1865 /* Sanity check */
1807 printk(KERN_WARNING 1866 printk(KERN_WARNING