diff options
Diffstat (limited to 'fs/binfmt_elf.c')
-rw-r--r-- | fs/binfmt_elf.c | 151 |
1 files changed, 90 insertions, 61 deletions
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index fd5b2ea5d299..535e763ab1a6 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/random.h> | 31 | #include <linux/random.h> |
32 | #include <linux/elf.h> | 32 | #include <linux/elf.h> |
33 | #include <linux/utsname.h> | 33 | #include <linux/utsname.h> |
34 | #include <linux/coredump.h> | ||
34 | #include <asm/uaccess.h> | 35 | #include <asm/uaccess.h> |
35 | #include <asm/param.h> | 36 | #include <asm/param.h> |
36 | #include <asm/page.h> | 37 | #include <asm/page.h> |
@@ -1085,36 +1086,6 @@ out: | |||
1085 | * Modelled on fs/exec.c:aout_core_dump() | 1086 | * Modelled on fs/exec.c:aout_core_dump() |
1086 | * Jeremy Fitzhardinge <jeremy@sw.oz.au> | 1087 | * Jeremy Fitzhardinge <jeremy@sw.oz.au> |
1087 | */ | 1088 | */ |
1088 | /* | ||
1089 | * These are the only things you should do on a core-file: use only these | ||
1090 | * functions to write out all the necessary info. | ||
1091 | */ | ||
1092 | static int dump_write(struct file *file, const void *addr, int nr) | ||
1093 | { | ||
1094 | return file->f_op->write(file, addr, nr, &file->f_pos) == nr; | ||
1095 | } | ||
1096 | |||
1097 | static int dump_seek(struct file *file, loff_t off) | ||
1098 | { | ||
1099 | if (file->f_op->llseek && file->f_op->llseek != no_llseek) { | ||
1100 | if (file->f_op->llseek(file, off, SEEK_CUR) < 0) | ||
1101 | return 0; | ||
1102 | } else { | ||
1103 | char *buf = (char *)get_zeroed_page(GFP_KERNEL); | ||
1104 | if (!buf) | ||
1105 | return 0; | ||
1106 | while (off > 0) { | ||
1107 | unsigned long n = off; | ||
1108 | if (n > PAGE_SIZE) | ||
1109 | n = PAGE_SIZE; | ||
1110 | if (!dump_write(file, buf, n)) | ||
1111 | return 0; | ||
1112 | off -= n; | ||
1113 | } | ||
1114 | free_page((unsigned long)buf); | ||
1115 | } | ||
1116 | return 1; | ||
1117 | } | ||
1118 | 1089 | ||
1119 | /* | 1090 | /* |
1120 | * Decide what to dump of a segment, part, all or none. | 1091 | * Decide what to dump of a segment, part, all or none. |
@@ -1249,11 +1220,6 @@ static int writenote(struct memelfnote *men, struct file *file, | |||
1249 | } | 1220 | } |
1250 | #undef DUMP_WRITE | 1221 | #undef DUMP_WRITE |
1251 | 1222 | ||
1252 | #define DUMP_WRITE(addr, nr) \ | ||
1253 | if ((size += (nr)) > cprm->limit || \ | ||
1254 | !dump_write(cprm->file, (addr), (nr))) \ | ||
1255 | goto end_coredump; | ||
1256 | |||
1257 | static void fill_elf_header(struct elfhdr *elf, int segs, | 1223 | static void fill_elf_header(struct elfhdr *elf, int segs, |
1258 | u16 machine, u32 flags, u8 osabi) | 1224 | u16 machine, u32 flags, u8 osabi) |
1259 | { | 1225 | { |
@@ -1872,6 +1838,34 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma, | |||
1872 | return gate_vma; | 1838 | return gate_vma; |
1873 | } | 1839 | } |
1874 | 1840 | ||
1841 | static 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 | |||
1857 | static 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 | |||
1875 | /* | 1869 | /* |
1876 | * Actual dumper | 1870 | * Actual dumper |
1877 | * | 1871 | * |
@@ -1888,8 +1882,11 @@ static int elf_core_dump(struct coredump_params *cprm) | |||
1888 | struct vm_area_struct *vma, *gate_vma; | 1882 | struct vm_area_struct *vma, *gate_vma; |
1889 | struct elfhdr *elf = NULL; | 1883 | struct elfhdr *elf = NULL; |
1890 | loff_t offset = 0, dataoff, foffset; | 1884 | loff_t offset = 0, dataoff, foffset; |
1891 | unsigned long mm_flags; | ||
1892 | struct elf_note_info info; | 1885 | struct elf_note_info info; |
1886 | struct elf_phdr *phdr4note = NULL; | ||
1887 | struct elf_shdr *shdr4extnum = NULL; | ||
1888 | Elf_Half e_phnum; | ||
1889 | elf_addr_t e_shoff; | ||
1893 | 1890 | ||
1894 | /* | 1891 | /* |
1895 | * We no longer stop all VM operations. | 1892 | * We no longer stop all VM operations. |
@@ -1912,20 +1909,25 @@ static int elf_core_dump(struct coredump_params *cprm) | |||
1912 | * Please check DEFAULT_MAX_MAP_COUNT definition when you modify here. | 1909 | * Please check DEFAULT_MAX_MAP_COUNT definition when you modify here. |
1913 | */ | 1910 | */ |
1914 | segs = current->mm->map_count; | 1911 | segs = current->mm->map_count; |
1915 | #ifdef ELF_CORE_EXTRA_PHDRS | 1912 | segs += elf_core_extra_phdrs(); |
1916 | segs += ELF_CORE_EXTRA_PHDRS; | ||
1917 | #endif | ||
1918 | 1913 | ||
1919 | gate_vma = get_gate_vma(current); | 1914 | gate_vma = get_gate_vma(current); |
1920 | if (gate_vma != NULL) | 1915 | if (gate_vma != NULL) |
1921 | segs++; | 1916 | segs++; |
1922 | 1917 | ||
1918 | /* for notes section */ | ||
1919 | segs++; | ||
1920 | |||
1921 | /* If segs > PN_XNUM(0xffff), then e_phnum overflows. To avoid | ||
1922 | * this, kernel supports extended numbering. Have a look at | ||
1923 | * include/linux/elf.h for further information. */ | ||
1924 | e_phnum = segs > PN_XNUM ? PN_XNUM : segs; | ||
1925 | |||
1923 | /* | 1926 | /* |
1924 | * Collect all the non-memory information about the process for the | 1927 | * Collect all the non-memory information about the process for the |
1925 | * notes. This also sets up the file header. | 1928 | * notes. This also sets up the file header. |
1926 | */ | 1929 | */ |
1927 | if (!fill_note_info(elf, segs + 1, /* including notes section */ | 1930 | if (!fill_note_info(elf, e_phnum, &info, cprm->signr, cprm->regs)) |
1928 | &info, cprm->signr, cprm->regs)) | ||
1929 | goto cleanup; | 1931 | goto cleanup; |
1930 | 1932 | ||
1931 | has_dumped = 1; | 1933 | has_dumped = 1; |
@@ -1934,31 +1936,47 @@ static int elf_core_dump(struct coredump_params *cprm) | |||
1934 | fs = get_fs(); | 1936 | fs = get_fs(); |
1935 | set_fs(KERNEL_DS); | 1937 | set_fs(KERNEL_DS); |
1936 | 1938 | ||
1937 | DUMP_WRITE(elf, sizeof(*elf)); | ||
1938 | offset += sizeof(*elf); /* Elf header */ | 1939 | offset += sizeof(*elf); /* Elf header */ |
1939 | offset += (segs + 1) * sizeof(struct elf_phdr); /* Program headers */ | 1940 | offset += segs * sizeof(struct elf_phdr); /* Program headers */ |
1940 | foffset = offset; | 1941 | foffset = offset; |
1941 | 1942 | ||
1942 | /* Write notes phdr entry */ | 1943 | /* Write notes phdr entry */ |
1943 | { | 1944 | { |
1944 | struct elf_phdr phdr; | ||
1945 | size_t sz = get_note_info_size(&info); | 1945 | size_t sz = get_note_info_size(&info); |
1946 | 1946 | ||
1947 | sz += elf_coredump_extra_notes_size(); | 1947 | sz += elf_coredump_extra_notes_size(); |
1948 | 1948 | ||
1949 | fill_elf_note_phdr(&phdr, sz, offset); | 1949 | phdr4note = kmalloc(sizeof(*phdr4note), GFP_KERNEL); |
1950 | if (!phdr4note) | ||
1951 | goto end_coredump; | ||
1952 | |||
1953 | fill_elf_note_phdr(phdr4note, sz, offset); | ||
1950 | offset += sz; | 1954 | offset += sz; |
1951 | DUMP_WRITE(&phdr, sizeof(phdr)); | ||
1952 | } | 1955 | } |
1953 | 1956 | ||
1954 | dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); | 1957 | dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); |
1955 | 1958 | ||
1956 | /* | 1959 | offset += elf_core_vma_data_size(gate_vma, cprm->mm_flags); |
1957 | * We must use the same mm->flags while dumping core to avoid | 1960 | offset += elf_core_extra_data_size(); |
1958 | * inconsistency between the program headers and bodies, otherwise an | 1961 | e_shoff = offset; |
1959 | * unusable core file can be generated. | 1962 | |
1960 | */ | 1963 | if (e_phnum == PN_XNUM) { |
1961 | mm_flags = current->mm->flags; | 1964 | shdr4extnum = kmalloc(sizeof(*shdr4extnum), GFP_KERNEL); |
1965 | if (!shdr4extnum) | ||
1966 | goto end_coredump; | ||
1967 | fill_extnum_info(elf, shdr4extnum, e_shoff, segs); | ||
1968 | } | ||
1969 | |||
1970 | offset = dataoff; | ||
1971 | |||
1972 | size += sizeof(*elf); | ||
1973 | if (size > cprm->limit || !dump_write(cprm->file, elf, sizeof(*elf))) | ||
1974 | goto end_coredump; | ||
1975 | |||
1976 | size += sizeof(*phdr4note); | ||
1977 | if (size > cprm->limit | ||
1978 | || !dump_write(cprm->file, phdr4note, sizeof(*phdr4note))) | ||
1979 | goto end_coredump; | ||
1962 | 1980 | ||
1963 | /* Write program headers for segments dump */ | 1981 | /* Write program headers for segments dump */ |
1964 | for (vma = first_vma(current, gate_vma); vma != NULL; | 1982 | for (vma = first_vma(current, gate_vma); vma != NULL; |
@@ -1969,7 +1987,7 @@ static int elf_core_dump(struct coredump_params *cprm) | |||
1969 | phdr.p_offset = offset; | 1987 | phdr.p_offset = offset; |
1970 | phdr.p_vaddr = vma->vm_start; | 1988 | phdr.p_vaddr = vma->vm_start; |
1971 | phdr.p_paddr = 0; | 1989 | phdr.p_paddr = 0; |
1972 | phdr.p_filesz = vma_dump_size(vma, mm_flags); | 1990 | phdr.p_filesz = vma_dump_size(vma, cprm->mm_flags); |
1973 | phdr.p_memsz = vma->vm_end - vma->vm_start; | 1991 | phdr.p_memsz = vma->vm_end - vma->vm_start; |
1974 | offset += phdr.p_filesz; | 1992 | offset += phdr.p_filesz; |
1975 | phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; | 1993 | phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; |
@@ -1979,12 +1997,14 @@ static int elf_core_dump(struct coredump_params *cprm) | |||
1979 | phdr.p_flags |= PF_X; | 1997 | phdr.p_flags |= PF_X; |
1980 | phdr.p_align = ELF_EXEC_PAGESIZE; | 1998 | phdr.p_align = ELF_EXEC_PAGESIZE; |
1981 | 1999 | ||
1982 | DUMP_WRITE(&phdr, sizeof(phdr)); | 2000 | size += sizeof(phdr); |
2001 | if (size > cprm->limit | ||
2002 | || !dump_write(cprm->file, &phdr, sizeof(phdr))) | ||
2003 | goto end_coredump; | ||
1983 | } | 2004 | } |
1984 | 2005 | ||
1985 | #ifdef ELF_CORE_WRITE_EXTRA_PHDRS | 2006 | if (!elf_core_write_extra_phdrs(cprm->file, offset, &size, cprm->limit)) |
1986 | ELF_CORE_WRITE_EXTRA_PHDRS; | 2007 | goto end_coredump; |
1987 | #endif | ||
1988 | 2008 | ||
1989 | /* write out the notes section */ | 2009 | /* write out the notes section */ |
1990 | if (!write_note_info(&info, cprm->file, &foffset)) | 2010 | if (!write_note_info(&info, cprm->file, &foffset)) |
@@ -2002,7 +2022,7 @@ static int elf_core_dump(struct coredump_params *cprm) | |||
2002 | unsigned long addr; | 2022 | unsigned long addr; |
2003 | unsigned long end; | 2023 | unsigned long end; |
2004 | 2024 | ||
2005 | end = vma->vm_start + vma_dump_size(vma, mm_flags); | 2025 | end = vma->vm_start + vma_dump_size(vma, cprm->mm_flags); |
2006 | 2026 | ||
2007 | for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) { | 2027 | for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) { |
2008 | struct page *page; | 2028 | struct page *page; |
@@ -2023,15 +2043,24 @@ static int elf_core_dump(struct coredump_params *cprm) | |||
2023 | } | 2043 | } |
2024 | } | 2044 | } |
2025 | 2045 | ||
2026 | #ifdef ELF_CORE_WRITE_EXTRA_DATA | 2046 | if (!elf_core_write_extra_data(cprm->file, &size, cprm->limit)) |
2027 | ELF_CORE_WRITE_EXTRA_DATA; | 2047 | goto end_coredump; |
2028 | #endif | 2048 | |
2049 | if (e_phnum == PN_XNUM) { | ||
2050 | size += sizeof(*shdr4extnum); | ||
2051 | if (size > cprm->limit | ||
2052 | || !dump_write(cprm->file, shdr4extnum, | ||
2053 | sizeof(*shdr4extnum))) | ||
2054 | goto end_coredump; | ||
2055 | } | ||
2029 | 2056 | ||
2030 | end_coredump: | 2057 | end_coredump: |
2031 | set_fs(fs); | 2058 | set_fs(fs); |
2032 | 2059 | ||
2033 | cleanup: | 2060 | cleanup: |
2034 | free_note_info(&info); | 2061 | free_note_info(&info); |
2062 | kfree(shdr4extnum); | ||
2063 | kfree(phdr4note); | ||
2035 | kfree(elf); | 2064 | kfree(elf); |
2036 | out: | 2065 | out: |
2037 | return has_dumped; | 2066 | return has_dumped; |