diff options
author | Jungseung Lee <js07.lee@gmail.com> | 2014-12-10 18:52:16 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-10 20:41:12 -0500 |
commit | 52f5592e549c013feb9bb71cab3e6fd624633577 (patch) | |
tree | 55bdcdd7e82121f8c8e071562917ce9dbc964efe | |
parent | f7e1ad1a1e23af97419cd8d5adff67fedf7cf169 (diff) |
fs/binfmt_elf.c: fix internal inconsistency relating to vma dump size
vma_dump_size() has been used several times on actual dumper and it is
supposed to return the same value for the same vma. But vma_dump_size()
could return different values for same vma.
The known problem case is concurrent shared memory removal. If a vma is
used for a shared memory and that shared memory is removed between
writing program header and dumping vma memory, this will result in a
dump file which is internally consistent.
To fix the problem, we set baseline to get dump size and store the size
into vma_filesz and always use the same vma dump size which is stored in
vma_filsz. The consistnecy with reality is not actually guranteed, but
it's tolerable since that is fully consistent with base line.
Signed-off-by: Jungseung Lee <js07.lee@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/binfmt_elf.c | 40 |
1 files changed, 22 insertions, 18 deletions
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index d8fc0605b9d2..3a6175fe10c0 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c | |||
@@ -1994,18 +1994,6 @@ static void fill_extnum_info(struct elfhdr *elf, struct elf_shdr *shdr4extnum, | |||
1994 | shdr4extnum->sh_info = segs; | 1994 | shdr4extnum->sh_info = segs; |
1995 | } | 1995 | } |
1996 | 1996 | ||
1997 | static size_t elf_core_vma_data_size(struct vm_area_struct *gate_vma, | ||
1998 | unsigned long mm_flags) | ||
1999 | { | ||
2000 | struct vm_area_struct *vma; | ||
2001 | size_t size = 0; | ||
2002 | |||
2003 | for (vma = first_vma(current, gate_vma); vma != NULL; | ||
2004 | vma = next_vma(vma, gate_vma)) | ||
2005 | size += vma_dump_size(vma, mm_flags); | ||
2006 | return size; | ||
2007 | } | ||
2008 | |||
2009 | /* | 1997 | /* |
2010 | * Actual dumper | 1998 | * Actual dumper |
2011 | * | 1999 | * |
@@ -2017,7 +2005,8 @@ static int elf_core_dump(struct coredump_params *cprm) | |||
2017 | { | 2005 | { |
2018 | int has_dumped = 0; | 2006 | int has_dumped = 0; |
2019 | mm_segment_t fs; | 2007 | mm_segment_t fs; |
2020 | int segs; | 2008 | int segs, i; |
2009 | size_t vma_data_size = 0; | ||
2021 | struct vm_area_struct *vma, *gate_vma; | 2010 | struct vm_area_struct *vma, *gate_vma; |
2022 | struct elfhdr *elf = NULL; | 2011 | struct elfhdr *elf = NULL; |
2023 | loff_t offset = 0, dataoff; | 2012 | loff_t offset = 0, dataoff; |
@@ -2026,6 +2015,7 @@ static int elf_core_dump(struct coredump_params *cprm) | |||
2026 | struct elf_shdr *shdr4extnum = NULL; | 2015 | struct elf_shdr *shdr4extnum = NULL; |
2027 | Elf_Half e_phnum; | 2016 | Elf_Half e_phnum; |
2028 | elf_addr_t e_shoff; | 2017 | elf_addr_t e_shoff; |
2018 | elf_addr_t *vma_filesz = NULL; | ||
2029 | 2019 | ||
2030 | /* | 2020 | /* |
2031 | * We no longer stop all VM operations. | 2021 | * We no longer stop all VM operations. |
@@ -2093,7 +2083,20 @@ static int elf_core_dump(struct coredump_params *cprm) | |||
2093 | 2083 | ||
2094 | dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); | 2084 | dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); |
2095 | 2085 | ||
2096 | offset += elf_core_vma_data_size(gate_vma, cprm->mm_flags); | 2086 | vma_filesz = kmalloc_array(segs - 1, sizeof(*vma_filesz), GFP_KERNEL); |
2087 | if (!vma_filesz) | ||
2088 | goto end_coredump; | ||
2089 | |||
2090 | for (i = 0, vma = first_vma(current, gate_vma); vma != NULL; | ||
2091 | vma = next_vma(vma, gate_vma)) { | ||
2092 | unsigned long dump_size; | ||
2093 | |||
2094 | dump_size = vma_dump_size(vma, cprm->mm_flags); | ||
2095 | vma_filesz[i++] = dump_size; | ||
2096 | vma_data_size += dump_size; | ||
2097 | } | ||
2098 | |||
2099 | offset += vma_data_size; | ||
2097 | offset += elf_core_extra_data_size(); | 2100 | offset += elf_core_extra_data_size(); |
2098 | e_shoff = offset; | 2101 | e_shoff = offset; |
2099 | 2102 | ||
@@ -2113,7 +2116,7 @@ static int elf_core_dump(struct coredump_params *cprm) | |||
2113 | goto end_coredump; | 2116 | goto end_coredump; |
2114 | 2117 | ||
2115 | /* Write program headers for segments dump */ | 2118 | /* Write program headers for segments dump */ |
2116 | for (vma = first_vma(current, gate_vma); vma != NULL; | 2119 | for (i = 0, vma = first_vma(current, gate_vma); vma != NULL; |
2117 | vma = next_vma(vma, gate_vma)) { | 2120 | vma = next_vma(vma, gate_vma)) { |
2118 | struct elf_phdr phdr; | 2121 | struct elf_phdr phdr; |
2119 | 2122 | ||
@@ -2121,7 +2124,7 @@ static int elf_core_dump(struct coredump_params *cprm) | |||
2121 | phdr.p_offset = offset; | 2124 | phdr.p_offset = offset; |
2122 | phdr.p_vaddr = vma->vm_start; | 2125 | phdr.p_vaddr = vma->vm_start; |
2123 | phdr.p_paddr = 0; | 2126 | phdr.p_paddr = 0; |
2124 | phdr.p_filesz = vma_dump_size(vma, cprm->mm_flags); | 2127 | phdr.p_filesz = vma_filesz[i++]; |
2125 | phdr.p_memsz = vma->vm_end - vma->vm_start; | 2128 | phdr.p_memsz = vma->vm_end - vma->vm_start; |
2126 | offset += phdr.p_filesz; | 2129 | offset += phdr.p_filesz; |
2127 | phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; | 2130 | phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; |
@@ -2149,12 +2152,12 @@ static int elf_core_dump(struct coredump_params *cprm) | |||
2149 | if (!dump_skip(cprm, dataoff - cprm->written)) | 2152 | if (!dump_skip(cprm, dataoff - cprm->written)) |
2150 | goto end_coredump; | 2153 | goto end_coredump; |
2151 | 2154 | ||
2152 | for (vma = first_vma(current, gate_vma); vma != NULL; | 2155 | for (i = 0, vma = first_vma(current, gate_vma); vma != NULL; |
2153 | vma = next_vma(vma, gate_vma)) { | 2156 | vma = next_vma(vma, gate_vma)) { |
2154 | unsigned long addr; | 2157 | unsigned long addr; |
2155 | unsigned long end; | 2158 | unsigned long end; |
2156 | 2159 | ||
2157 | end = vma->vm_start + vma_dump_size(vma, cprm->mm_flags); | 2160 | end = vma->vm_start + vma_filesz[i++]; |
2158 | 2161 | ||
2159 | for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) { | 2162 | for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) { |
2160 | struct page *page; | 2163 | struct page *page; |
@@ -2187,6 +2190,7 @@ end_coredump: | |||
2187 | cleanup: | 2190 | cleanup: |
2188 | free_note_info(&info); | 2191 | free_note_info(&info); |
2189 | kfree(shdr4extnum); | 2192 | kfree(shdr4extnum); |
2193 | kfree(vma_filesz); | ||
2190 | kfree(phdr4note); | 2194 | kfree(phdr4note); |
2191 | kfree(elf); | 2195 | kfree(elf); |
2192 | out: | 2196 | out: |