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; | 
