This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC] libelf: segment fault on x86-64 while file's bss offset have a large number


*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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]