*Environment cat /etc/issue Ubuntu 13.04 \n \l
uname -a Linux pek-hjia-d1 3.8.0-31-generic #46-Ubuntu SMP Tue Sep 10 20:03:44 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
*Problem 1) Here is the test source code $ cat >> test.c << EOF
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h>
#include "libelf.h"
int main(int argc, char *argv[]) { int fd; Elf *e;
if (elf_version(EV_CURRENT) == EV_NONE) { printf ("library out of date\n"); exit (1); }
if ((fd = open("test/xB.linkhuge", O_RDWR)) < 0) { printf("%s %d failed\n", __FUNCTION__, __LINE__); exit (1); }
if ((e = elf_begin(fd, ELF_C_RDWR_MMAP, (Elf *) 0)) == 0) { printf("failed %s", elf_errmsg (-1)); exit (1); }
elf_flagelf (e, ELF_C_SET, ELF_F_LAYOUT); elf_update(e, ELF_C_WRITE); elf_end(e); close(fd); } EOF
2) Download http://kojipkgs.fedoraproject.org/packages/elfutils/0.157/2.fc21/x86_64/elfu... to get libelf.a for debug.
Download http://kojipkgs.fedoraproject.org/packages/elfutils/0.157/2.fc21/x86_64/elfu... to get libelf.h for debug.
3) Compile test.c with libelf.a $ gcc test.c -o test_case -static -L. -lelf
4) Prepare file whose bss offset have a large number '00200000' Download the attachment from https://bugzilla.redhat.com/show_bug.cgi?id=1020842
$ ls test/xB.linkhuge -al -rwxr-xr-x 1 jiahongxu jiahongxu 1221403 Oct 18 18:55 test/xB.linkhuge
$ readelf -a xB.linkhuge ...... [Nr] Name Type Address Offset Size EntSize Flags Link Info Align skip.. [25] .data PROGBITS 00000000005128a0 001128a0 0000000000010168 0000000000000000 WA 0 0 32 [26] .bss NOBITS 0000000001000000 00200000 0000000000010050 0000000000000000 WA 0 0 32 [27] .comment PROGBITS 0000000000000000 00122a08 0000000000000011 0000000000000001 MS 0 0 1 ......
5) Run test_case with strace, there was mmap/munmap error. $ strace ./test_case execve("./test_case", ["./test_case"], [/* 59 vars */]) = 0 uname({sys="Linux", node="pek-hjia-d1", ...}) = 0 brk(0) = 0x16a6000 brk(0x16a71c0) = 0x16a71c0 arch_prctl(ARCH_SET_FS, 0x16a6880) = 0 brk(0x16c81c0) = 0x16c81c0 brk(0x16c9000) = 0x16c9000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) open("test/xB.linkhuge", O_RDWR) = 3 fcntl(3, F_GETFL) = 0x8002 (flags O_RDWR|O_LARGEFILE) fstat(3, {st_mode=S_IFREG|0755, st_size=1221403, ...}) = 0 mmap(NULL, 1221403, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7ff720fe2000 fstat(3, {st_mode=S_IFREG|0755, st_size=1221403, ...}) = 0 ftruncate(3, 2097152) = 0 msync(0x7ff720fe2000, 1216568, MS_SYNC) = 0 munmap(0x7ff720fe2000, 2097152) = 0 close(3) = 0 exit_group(0) = ?
6) $ ls test/xB.linkhuge -al -rwxr-xr-x 1 jiahongxu jiahongxu 2097152 Oct 18 19:04 test/xB.linkhuge
*Analysis
1) While ELF_C_RDWR_MMAP was used, elf_begin invoked mmap() to map file into memory with the size of '1221403'. ...strace log... mmap(NULL, 1221403, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7ff720fe2000 ................
2) While 'xB.linkhuge' bss Offset has a large number '00200000', elf_update caculated file size by __elf64_updatenull_wrlock and the size was enlarged from '1221403' to '2097152'
3) In this situation, elf_update invoked ftruncate to enlarge the file, and memory size (elf->maximum_size) also was incorrectly updated. ...strace log... ftruncate(3, 2097152) ................
4) There was segment fault in elf_end which invoked munmap with the length is the enlarged file size '2097152', not the length of mmap '1216568'. ...strace log... munmap(0x7ff720fe2000, 2097152) = 0 ................
*Solution
1) I tried to modify elf_update.c, don't update memory size (elf->maximum_size) in this situation. It fixed this issue and everything looks ok, but I am not sure the modification is necessary. ...... 11 diff --git a/libelf/elf_update.c b/libelf/elf_update.c 12 --- a/libelf/elf_update.c 13 +++ b/libelf/elf_update.c 14 @@ -120,7 +120,9 @@ write_file (Elf *elf, off_t size, int change_bo, size_t shnum) 15 size = -1; 16 } 17 18 - if (size != -1 && elf->parent == NULL) 19 + /* If the file is enlarged by truncate, we should not update maximum_size to 20 + avoid segment fault while invoking munmap in elf_end */ 21 + if (size != -1 && elf->parent == NULL && (size_t) size <= elf->maximum_size) 22 elf->maximum_size = size; ......
2) I also tried to add check before munmap in elf_end by msync with the length of elf->maximum_size, if msync return error, munmap should not be invoked, this could avoid segment fault.
--- a/libelf/elf_end.c +++ b/libelf/elf_end.c @@ -217,7 +217,10 @@ elf_end (elf) if ((elf->flags & ELF_F_MALLOCED) != 0) free (elf->map_address); else if ((elf->flags & ELF_F_MMAPPED) != 0) - munmap (elf->map_address, elf->maximum_size); + { + if (msync (elf->map_address, elf->maximum_size, MS_SYNC) == 0) + munmap (elf->map_address, elf->maximum_size); + } }
3) Any suggestion is welcomed.
Hi,
Apologies for the very late reply. And thanks for your example. I can replicate your results, and there might indeed be some problem seeing the file size increase. But I cannot replicate the segment fault, nor do I see any errors with mmap/mmunmap.
On Fri, Oct 18, 2013 at 07:49:43PM +0800, Hongxu Jia wrote:
- Run test_case with strace, there was mmap/munmap error.
$ strace ./test_case [...] open("test/xB.linkhuge", O_RDWR) = 3 fcntl(3, F_GETFL) = 0x8002 (flags O_RDWR|O_LARGEFILE) fstat(3, {st_mode=S_IFREG|0755, st_size=1221403, ...}) = 0 mmap(NULL, 1221403, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7ff720fe2000 fstat(3, {st_mode=S_IFREG|0755, st_size=1221403, ...}) = 0 ftruncate(3, 2097152) = 0 msync(0x7ff720fe2000, 1216568, MS_SYNC) = 0 munmap(0x7ff720fe2000, 2097152) = 0 close(3) = 0 exit_group(0) = ?
I see the same. So the file was indeed increased in size by the ftruncate call. But it looks like mmap, msync and munmap all succeed (return zero) just fine.
I think the example is too small to show the actual bug/error.
Your analysis seems correct, we mmap the original file size, then we ftruncate to increase the file size, then msync and munmap with this new size. Which seems to succeed, so I assume the ftruncate did also extend the mapped size (but I cannot immediate find whether that is really true). It does look something is a little odd here. But I cannot immediately see the crash myself. Do you happen to have more examples of this failure?
Thanks,
Mark
elfutils-devel@lists.fedorahosted.org