diff options
Diffstat (limited to 'fs/binfmt_elf_fdpic.c')
| -rw-r--r-- | fs/binfmt_elf_fdpic.c | 183 |
1 files changed, 113 insertions, 70 deletions
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 18d77297ccc8..7ab23e006e4c 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c | |||
| @@ -34,6 +34,7 @@ | |||
| 34 | #include <linux/elf.h> | 34 | #include <linux/elf.h> |
| 35 | #include <linux/elf-fdpic.h> | 35 | #include <linux/elf-fdpic.h> |
| 36 | #include <linux/elfcore.h> | 36 | #include <linux/elfcore.h> |
| 37 | #include <linux/coredump.h> | ||
| 37 | 38 | ||
| 38 | #include <asm/uaccess.h> | 39 | #include <asm/uaccess.h> |
| 39 | #include <asm/param.h> | 40 | #include <asm/param.h> |
| @@ -1216,26 +1217,6 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params, | |||
| 1216 | #ifdef CONFIG_ELF_CORE | 1217 | #ifdef CONFIG_ELF_CORE |
| 1217 | 1218 | ||
| 1218 | /* | 1219 | /* |
| 1219 | * These are the only things you should do on a core-file: use only these | ||
| 1220 | * functions to write out all the necessary info. | ||
| 1221 | */ | ||
| 1222 | static int dump_write(struct file *file, const void *addr, int nr) | ||
| 1223 | { | ||
| 1224 | return file->f_op->write(file, addr, nr, &file->f_pos) == nr; | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | static int dump_seek(struct file *file, loff_t off) | ||
| 1228 | { | ||
| 1229 | if (file->f_op->llseek) { | ||
| 1230 | if (file->f_op->llseek(file, off, SEEK_SET) != off) | ||
| 1231 | return 0; | ||
| 1232 | } else { | ||
| 1233 | file->f_pos = off; | ||
| 1234 | } | ||
| 1235 | return 1; | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | /* | ||
| 1239 | * Decide whether a segment is worth dumping; default is yes to be | 1220 | * Decide whether a segment is worth dumping; default is yes to be |
| 1240 | * sure (missing info is worse than too much; etc). | 1221 | * sure (missing info is worse than too much; etc). |
| 1241 | * Personally I'd include everything, and use the coredump limit... | 1222 | * Personally I'd include everything, and use the coredump limit... |
| @@ -1313,35 +1294,35 @@ static int notesize(struct memelfnote *en) | |||
| 1313 | 1294 | ||
| 1314 | /* #define DEBUG */ | 1295 | /* #define DEBUG */ |
| 1315 | 1296 | ||
| 1316 | #define DUMP_WRITE(addr, nr) \ | 1297 | #define DUMP_WRITE(addr, nr, foffset) \ |
| 1317 | do { if (!dump_write(file, (addr), (nr))) return 0; } while(0) | 1298 | 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 | 1299 | ||
| 1321 | static int writenote(struct memelfnote *men, struct file *file) | 1300 | static int alignfile(struct file *file, loff_t *foffset) |
| 1322 | { | 1301 | { |
| 1323 | struct elf_note en; | 1302 | static const char buf[4] = { 0, }; |
| 1303 | DUMP_WRITE(buf, roundup(*foffset, 4) - *foffset, foffset); | ||
| 1304 | return 1; | ||
| 1305 | } | ||
| 1324 | 1306 | ||
| 1307 | static int writenote(struct memelfnote *men, struct file *file, | ||
| 1308 | loff_t *foffset) | ||
| 1309 | { | ||
| 1310 | struct elf_note en; | ||
| 1325 | en.n_namesz = strlen(men->name) + 1; | 1311 | en.n_namesz = strlen(men->name) + 1; |
| 1326 | en.n_descsz = men->datasz; | 1312 | en.n_descsz = men->datasz; |
| 1327 | en.n_type = men->type; | 1313 | en.n_type = men->type; |
| 1328 | 1314 | ||
| 1329 | DUMP_WRITE(&en, sizeof(en)); | 1315 | DUMP_WRITE(&en, sizeof(en), foffset); |
| 1330 | DUMP_WRITE(men->name, en.n_namesz); | 1316 | DUMP_WRITE(men->name, en.n_namesz, foffset); |
| 1331 | /* XXX - cast from long long to long to avoid need for libgcc.a */ | 1317 | if (!alignfile(file, foffset)) |
| 1332 | DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */ | 1318 | return 0; |
| 1333 | DUMP_WRITE(men->data, men->datasz); | 1319 | DUMP_WRITE(men->data, men->datasz, foffset); |
| 1334 | DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */ | 1320 | if (!alignfile(file, foffset)) |
| 1321 | return 0; | ||
| 1335 | 1322 | ||
| 1336 | return 1; | 1323 | return 1; |
| 1337 | } | 1324 | } |
| 1338 | #undef DUMP_WRITE | 1325 | #undef DUMP_WRITE |
| 1339 | #undef DUMP_SEEK | ||
| 1340 | |||
| 1341 | #define DUMP_WRITE(addr, nr) \ | ||
| 1342 | if ((size += (nr)) > cprm->limit || \ | ||
| 1343 | !dump_write(cprm->file, (addr), (nr))) \ | ||
| 1344 | goto end_coredump; | ||
| 1345 | 1326 | ||
| 1346 | static inline void fill_elf_fdpic_header(struct elfhdr *elf, int segs) | 1327 | static inline void fill_elf_fdpic_header(struct elfhdr *elf, int segs) |
| 1347 | { | 1328 | { |
| @@ -1393,7 +1374,7 @@ static inline void fill_note(struct memelfnote *note, const char *name, int type | |||
| 1393 | 1374 | ||
| 1394 | /* | 1375 | /* |
| 1395 | * fill up all the fields in prstatus from the given task struct, except | 1376 | * fill up all the fields in prstatus from the given task struct, except |
| 1396 | * registers which need to be filled up seperately. | 1377 | * registers which need to be filled up separately. |
| 1397 | */ | 1378 | */ |
| 1398 | static void fill_prstatus(struct elf_prstatus *prstatus, | 1379 | static void fill_prstatus(struct elf_prstatus *prstatus, |
| 1399 | struct task_struct *p, long signr) | 1380 | struct task_struct *p, long signr) |
| @@ -1524,6 +1505,22 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t) | |||
| 1524 | return sz; | 1505 | return sz; |
| 1525 | } | 1506 | } |
| 1526 | 1507 | ||
| 1508 | static 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 | |||
| 1527 | /* | 1524 | /* |
| 1528 | * dump the segments for an MMU process | 1525 | * dump the segments for an MMU process |
| 1529 | */ | 1526 | */ |
| @@ -1552,7 +1549,7 @@ static int elf_fdpic_dump_segments(struct file *file, size_t *size, | |||
| 1552 | err = -EIO; | 1549 | err = -EIO; |
| 1553 | kunmap(page); | 1550 | kunmap(page); |
| 1554 | page_cache_release(page); | 1551 | page_cache_release(page); |
| 1555 | } else if (!dump_seek(file, file->f_pos + PAGE_SIZE)) | 1552 | } else if (!dump_seek(file, PAGE_SIZE)) |
| 1556 | err = -EFBIG; | 1553 | err = -EFBIG; |
| 1557 | if (err) | 1554 | if (err) |
| 1558 | goto out; | 1555 | goto out; |
| @@ -1588,6 +1585,17 @@ static int elf_fdpic_dump_segments(struct file *file, size_t *size, | |||
| 1588 | } | 1585 | } |
| 1589 | #endif | 1586 | #endif |
| 1590 | 1587 | ||
| 1588 | static 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 = vma->vm_next) | ||
| 1594 | if (maydump(vma, mm_flags)) | ||
| 1595 | size += vma->vm_end - vma->vm_start; | ||
| 1596 | return size; | ||
| 1597 | } | ||
| 1598 | |||
| 1591 | /* | 1599 | /* |
| 1592 | * Actual dumper | 1600 | * Actual dumper |
| 1593 | * | 1601 | * |
| @@ -1605,7 +1613,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) | |||
| 1605 | int i; | 1613 | int i; |
| 1606 | struct vm_area_struct *vma; | 1614 | struct vm_area_struct *vma; |
| 1607 | struct elfhdr *elf = NULL; | 1615 | struct elfhdr *elf = NULL; |
| 1608 | loff_t offset = 0, dataoff; | 1616 | loff_t offset = 0, dataoff, foffset; |
| 1609 | int numnote; | 1617 | int numnote; |
| 1610 | struct memelfnote *notes = NULL; | 1618 | struct memelfnote *notes = NULL; |
| 1611 | struct elf_prstatus *prstatus = NULL; /* NT_PRSTATUS */ | 1619 | struct elf_prstatus *prstatus = NULL; /* NT_PRSTATUS */ |
| @@ -1618,7 +1626,10 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) | |||
| 1618 | #endif | 1626 | #endif |
| 1619 | int thread_status_size = 0; | 1627 | int thread_status_size = 0; |
| 1620 | elf_addr_t *auxv; | 1628 | elf_addr_t *auxv; |
| 1621 | unsigned long mm_flags; | 1629 | struct elf_phdr *phdr4note = NULL; |
| 1630 | struct elf_shdr *shdr4extnum = NULL; | ||
| 1631 | Elf_Half e_phnum; | ||
| 1632 | elf_addr_t e_shoff; | ||
| 1622 | 1633 | ||
| 1623 | /* | 1634 | /* |
| 1624 | * We no longer stop all VM operations. | 1635 | * We no longer stop all VM operations. |
| @@ -1683,12 +1694,18 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) | |||
| 1683 | elf_core_copy_regs(&prstatus->pr_reg, cprm->regs); | 1694 | elf_core_copy_regs(&prstatus->pr_reg, cprm->regs); |
| 1684 | 1695 | ||
| 1685 | segs = current->mm->map_count; | 1696 | segs = current->mm->map_count; |
| 1686 | #ifdef ELF_CORE_EXTRA_PHDRS | 1697 | segs += elf_core_extra_phdrs(); |
| 1687 | segs += ELF_CORE_EXTRA_PHDRS; | 1698 | |
| 1688 | #endif | 1699 | /* for notes section */ |
| 1700 | segs++; | ||
| 1701 | |||
| 1702 | /* If segs > PN_XNUM(0xffff), then e_phnum overflows. To avoid | ||
| 1703 | * this, kernel supports extended numbering. Have a look at | ||
| 1704 | * include/linux/elf.h for further information. */ | ||
| 1705 | e_phnum = segs > PN_XNUM ? PN_XNUM : segs; | ||
| 1689 | 1706 | ||
| 1690 | /* Set up header */ | 1707 | /* Set up header */ |
| 1691 | fill_elf_fdpic_header(elf, segs + 1); /* including notes section */ | 1708 | fill_elf_fdpic_header(elf, e_phnum); |
| 1692 | 1709 | ||
| 1693 | has_dumped = 1; | 1710 | has_dumped = 1; |
| 1694 | current->flags |= PF_DUMPCORE; | 1711 | current->flags |= PF_DUMPCORE; |
| @@ -1727,13 +1744,12 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) | |||
| 1727 | fs = get_fs(); | 1744 | fs = get_fs(); |
| 1728 | set_fs(KERNEL_DS); | 1745 | set_fs(KERNEL_DS); |
| 1729 | 1746 | ||
| 1730 | 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 * sizeof(struct elf_phdr); /* Program headers */ |
| 1749 | foffset = offset; | ||
| 1733 | 1750 | ||
| 1734 | /* Write notes phdr entry */ | 1751 | /* Write notes phdr entry */ |
| 1735 | { | 1752 | { |
| 1736 | struct elf_phdr phdr; | ||
| 1737 | int sz = 0; | 1753 | int sz = 0; |
| 1738 | 1754 | ||
| 1739 | for (i = 0; i < numnote; i++) | 1755 | for (i = 0; i < numnote; i++) |
| @@ -1741,20 +1757,38 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) | |||
| 1741 | 1757 | ||
| 1742 | sz += thread_status_size; | 1758 | sz += thread_status_size; |
| 1743 | 1759 | ||
| 1744 | fill_elf_note_phdr(&phdr, sz, offset); | 1760 | phdr4note = kmalloc(sizeof(*phdr4note), GFP_KERNEL); |
| 1761 | if (!phdr4note) | ||
| 1762 | goto end_coredump; | ||
| 1763 | |||
| 1764 | fill_elf_note_phdr(phdr4note, sz, offset); | ||
| 1745 | offset += sz; | 1765 | offset += sz; |
| 1746 | DUMP_WRITE(&phdr, sizeof(phdr)); | ||
| 1747 | } | 1766 | } |
| 1748 | 1767 | ||
| 1749 | /* Page-align dumped data */ | 1768 | /* Page-align dumped data */ |
| 1750 | dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); | 1769 | dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); |
| 1751 | 1770 | ||
| 1752 | /* | 1771 | offset += elf_core_vma_data_size(cprm->mm_flags); |
| 1753 | * We must use the same mm->flags while dumping core to avoid | 1772 | offset += elf_core_extra_data_size(); |
| 1754 | * inconsistency between the program headers and bodies, otherwise an | 1773 | e_shoff = offset; |
| 1755 | * unusable core file can be generated. | 1774 | |
| 1756 | */ | 1775 | if (e_phnum == PN_XNUM) { |
| 1757 | mm_flags = current->mm->flags; | 1776 | shdr4extnum = kmalloc(sizeof(*shdr4extnum), GFP_KERNEL); |
| 1777 | if (!shdr4extnum) | ||
| 1778 | goto end_coredump; | ||
| 1779 | fill_extnum_info(elf, shdr4extnum, e_shoff, segs); | ||
| 1780 | } | ||
| 1781 | |||
| 1782 | offset = dataoff; | ||
| 1783 | |||
| 1784 | size += sizeof(*elf); | ||
| 1785 | if (size > cprm->limit || !dump_write(cprm->file, elf, sizeof(*elf))) | ||
| 1786 | goto end_coredump; | ||
| 1787 | |||
| 1788 | size += sizeof(*phdr4note); | ||
| 1789 | if (size > cprm->limit | ||
| 1790 | || !dump_write(cprm->file, phdr4note, sizeof(*phdr4note))) | ||
| 1791 | goto end_coredump; | ||
| 1758 | 1792 | ||
| 1759 | /* write program headers for segments dump */ | 1793 | /* write program headers for segments dump */ |
| 1760 | for (vma = current->mm->mmap; vma; vma = vma->vm_next) { | 1794 | for (vma = current->mm->mmap; vma; vma = vma->vm_next) { |
| @@ -1767,7 +1801,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) | |||
| 1767 | phdr.p_offset = offset; | 1801 | phdr.p_offset = offset; |
| 1768 | phdr.p_vaddr = vma->vm_start; | 1802 | phdr.p_vaddr = vma->vm_start; |
| 1769 | phdr.p_paddr = 0; | 1803 | phdr.p_paddr = 0; |
| 1770 | phdr.p_filesz = maydump(vma, mm_flags) ? sz : 0; | 1804 | phdr.p_filesz = maydump(vma, cprm->mm_flags) ? sz : 0; |
| 1771 | phdr.p_memsz = sz; | 1805 | phdr.p_memsz = sz; |
| 1772 | offset += phdr.p_filesz; | 1806 | offset += phdr.p_filesz; |
| 1773 | phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; | 1807 | phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; |
| @@ -1777,16 +1811,18 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) | |||
| 1777 | phdr.p_flags |= PF_X; | 1811 | phdr.p_flags |= PF_X; |
| 1778 | phdr.p_align = ELF_EXEC_PAGESIZE; | 1812 | phdr.p_align = ELF_EXEC_PAGESIZE; |
| 1779 | 1813 | ||
| 1780 | DUMP_WRITE(&phdr, sizeof(phdr)); | 1814 | size += sizeof(phdr); |
| 1815 | if (size > cprm->limit | ||
| 1816 | || !dump_write(cprm->file, &phdr, sizeof(phdr))) | ||
| 1817 | goto end_coredump; | ||
| 1781 | } | 1818 | } |
| 1782 | 1819 | ||
| 1783 | #ifdef ELF_CORE_WRITE_EXTRA_PHDRS | 1820 | if (!elf_core_write_extra_phdrs(cprm->file, offset, &size, cprm->limit)) |
| 1784 | ELF_CORE_WRITE_EXTRA_PHDRS; | 1821 | goto end_coredump; |
| 1785 | #endif | ||
| 1786 | 1822 | ||
| 1787 | /* write out the notes section */ | 1823 | /* write out the notes section */ |
| 1788 | for (i = 0; i < numnote; i++) | 1824 | for (i = 0; i < numnote; i++) |
| 1789 | if (!writenote(notes + i, cprm->file)) | 1825 | if (!writenote(notes + i, cprm->file, &foffset)) |
| 1790 | goto end_coredump; | 1826 | goto end_coredump; |
| 1791 | 1827 | ||
| 1792 | /* write out the thread status notes section */ | 1828 | /* write out the thread status notes section */ |
| @@ -1795,20 +1831,27 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) | |||
| 1795 | list_entry(t, struct elf_thread_status, list); | 1831 | list_entry(t, struct elf_thread_status, list); |
| 1796 | 1832 | ||
| 1797 | for (i = 0; i < tmp->num_notes; i++) | 1833 | for (i = 0; i < tmp->num_notes; i++) |
| 1798 | if (!writenote(&tmp->notes[i], cprm->file)) | 1834 | if (!writenote(&tmp->notes[i], cprm->file, &foffset)) |
| 1799 | goto end_coredump; | 1835 | goto end_coredump; |
| 1800 | } | 1836 | } |
| 1801 | 1837 | ||
| 1802 | if (!dump_seek(cprm->file, dataoff)) | 1838 | if (!dump_seek(cprm->file, dataoff - foffset)) |
| 1803 | goto end_coredump; | 1839 | goto end_coredump; |
| 1804 | 1840 | ||
| 1805 | if (elf_fdpic_dump_segments(cprm->file, &size, &cprm->limit, | 1841 | if (elf_fdpic_dump_segments(cprm->file, &size, &cprm->limit, |
| 1806 | mm_flags) < 0) | 1842 | cprm->mm_flags) < 0) |
| 1807 | goto end_coredump; | 1843 | goto end_coredump; |
| 1808 | 1844 | ||
| 1809 | #ifdef ELF_CORE_WRITE_EXTRA_DATA | 1845 | if (!elf_core_write_extra_data(cprm->file, &size, cprm->limit)) |
| 1810 | ELF_CORE_WRITE_EXTRA_DATA; | 1846 | goto end_coredump; |
| 1811 | #endif | 1847 | |
| 1848 | if (e_phnum == PN_XNUM) { | ||
| 1849 | size += sizeof(*shdr4extnum); | ||
| 1850 | if (size > cprm->limit | ||
| 1851 | || !dump_write(cprm->file, shdr4extnum, | ||
| 1852 | sizeof(*shdr4extnum))) | ||
| 1853 | goto end_coredump; | ||
| 1854 | } | ||
| 1812 | 1855 | ||
| 1813 | if (cprm->file->f_pos != offset) { | 1856 | if (cprm->file->f_pos != offset) { |
| 1814 | /* Sanity check */ | 1857 | /* Sanity check */ |
| @@ -1826,7 +1869,7 @@ cleanup: | |||
| 1826 | list_del(tmp); | 1869 | list_del(tmp); |
| 1827 | kfree(list_entry(tmp, struct elf_thread_status, list)); | 1870 | kfree(list_entry(tmp, struct elf_thread_status, list)); |
| 1828 | } | 1871 | } |
| 1829 | 1872 | kfree(phdr4note); | |
| 1830 | kfree(elf); | 1873 | kfree(elf); |
| 1831 | kfree(prstatus); | 1874 | kfree(prstatus); |
| 1832 | kfree(psinfo); | 1875 | kfree(psinfo); |
