*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/elfutils-libelf-devel-static-0.157-2.fc21.x86_64.rpm to get libelf.a for debug. Download http://kojipkgs.fedoraproject.org/packages/elfutils/0.157/2.fc21/x86_64/elfutils-libelf-devel-0.157-2.fc21.x86_64.rpm 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. -- 1.8.1.2