Bug 1020842 - libelf: segment fault on x86-64 while file's bss offset have a large number
libelf: segment fault on x86-64 while file's bss offset have a large number
Status: CLOSED ERRATA
Product: Fedora
Classification: Fedora
Component: elfutils (Show other bugs)
22
Unspecified Linux
unspecified Severity urgent
: ---
: ---
Assigned To: Roland McGrath
Fedora Extras Quality Assurance
: FutureFeature
: 1019707 (view as bug list)
Depends On:
Blocks:
  Show dependency treegraph
 
Reported: 2013-10-18 07:20 EDT by hongxu jia
Modified: 2015-07-28 21:35 EDT (History)
8 users (show)

See Also:
Fixed In Version: elfutils-0.163-1.fc21
Doc Type: Enhancement
Doc Text:
Story Points: ---
Clone Of:
Environment:
Last Closed: 2015-06-30 16:11:25 EDT
Type: Bug
Regression: ---
Mount Type: ---
Documentation: ---
CRM:
Verified Versions:
Category: ---
oVirt Team: ---
RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: ---


Attachments (Terms of Use)
The file whose bss offset have a large number (1.16 MB, application/x-executable)
2013-10-18 07:20 EDT, hongxu jia
no flags Details

  None (edit)
Description hongxu jia 2013-10-18 07:20:08 EDT
Created attachment 813722 [details]
The file whose 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'
$ 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
Comment 1 hongxu jia 2013-10-18 07:22:23 EDT
*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
   ................
Comment 2 hongxu jia 2013-10-18 07:33:37 EDT
*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.
Comment 3 Mark Wielaard 2013-12-09 04:41:14 EST
*** Bug 1019707 has been marked as a duplicate of this bug. ***
Comment 4 Jaroslav Reznik 2015-03-03 10:09:00 EST
This bug appears to have been reported against 'rawhide' during the Fedora 22 development cycle.
Changing version to '22'.

More information and reason for this action is here:
https://fedoraproject.org/wiki/Fedora_Program_Management/HouseKeeping/Fedora22
Comment 5 Mark Wielaard 2015-03-18 10:10:02 EDT
Still an issue with current upstream git elfutils.
Comment 6 Mark Wielaard 2015-03-18 15:26:42 EDT
So the problem is indeed the large sh_offset of the .bss section.
Since it is NOBITS the size of the section is taken into account.

gabi says that a NOBITS section occupies no space in the file, and its sh_offset member locates the conceptual placement in the file.

So that is why updatenull_wrlock takes the sh_offset and assumes it is inside the file (enlarging the file size) when ELF_F_LAYOUT is set.

We can probably just ignore the sh_offset of any NOBITS section in updatenull ().

diff --git a/libelf/elf32_updatenull.c b/libelf/elf32_updatenull.c
index 5e809b7..7629502 100644
--- a/libelf/elf32_updatenull.c
+++ b/libelf/elf32_updatenull.c
@@ -318,9 +318,8 @@ __elfw2(LIBELFBITS,updatenull_wrlock) (Elf *elf, int *change_bop, size_t shnum)
              if (elf->flags & ELF_F_LAYOUT)
                {
                  size = MAX ((GElf_Word) size,
-                             shdr->sh_offset
-                             + (shdr->sh_type != SHT_NOBITS
-                                ? shdr->sh_size : 0));
+                             (shdr->sh_type != SHT_NOBITS
+                              ? shdr->sh_offset + shdr->sh_size : 0));
 
                  /* The alignment must be a power of two.  This is a
                     requirement from the ELF specification.  Additionally


It seems to fix the issue.

Question. What program created the xB.linkhuge ELF file in the first place?
It seems it is bug to generate a .bss NOBITS section with a sh_off that is not inside the file in the first place (even if we can work around it). So maybe that generator should also be fixed?
Comment 7 Mark Wielaard 2015-03-18 15:31:59 EDT
(In reply to Mark Wielaard from comment #6)
> So the problem is indeed the large sh_offset of the .bss section.
> Since it is NOBITS the size of the section is taken into account.

Sorry, that should have read:

"Since it is NOBITS the size of the section is ignored, but the offset is taken into account."
Comment 8 Mark Wielaard 2015-03-23 17:17:56 EDT
Such files are probably created by buggy binutils ld:
https://sourceware.org/bugzilla/show_bug.cgi?id=12921
Comment 9 Mark Wielaard 2015-03-24 08:50:29 EDT
Patch posted upstream:
https://lists.fedorahosted.org/pipermail/elfutils-devel/2015-March/004646.html
Comment 10 Mark Wielaard 2015-06-08 08:32:32 EDT
The patch is now in elfutils git master and the bug will be fixed when elfutils 0.162 is released later this week.
Comment 11 Fedora Update System 2015-06-11 09:16:16 EDT
elfutils-0.162-1.fc22 has been submitted as an update for Fedora 22.
https://admin.fedoraproject.org/updates/elfutils-0.162-1.fc22
Comment 12 Fedora Update System 2015-06-13 02:35:42 EDT
Package elfutils-0.162-1.fc22:
* should fix your issue,
* was pushed to the Fedora 22 testing repository,
* should be available at your local mirror within two days.
Update it with:
# su -c 'yum update --enablerepo=updates-testing elfutils-0.162-1.fc22'
as soon as you are able to.
Please go to the following url:
https://admin.fedoraproject.org/updates/FEDORA-2015-9857/elfutils-0.162-1.fc22
then log in and leave karma (feedback).
Comment 13 Fedora Update System 2015-06-19 10:54:04 EDT
elfutils-0.163-1.fc22 has been submitted as an update for Fedora 22.
https://admin.fedoraproject.org/updates/elfutils-0.163-1.fc22
Comment 14 Fedora Update System 2015-06-30 16:11:25 EDT
elfutils-0.163-1.fc22 has been pushed to the Fedora 22 stable repository.  If problems still persist, please make note of it in this bug report.
Comment 15 Fedora Update System 2015-07-08 11:06:58 EDT
elfutils-0.163-1.fc21 has been submitted as an update for Fedora 21.
https://admin.fedoraproject.org/updates/elfutils-0.163-1.fc21
Comment 16 Fedora Update System 2015-07-28 21:35:44 EDT
elfutils-0.163-1.fc21 has been pushed to the Fedora 21 stable repository.  If problems still persist, please make note of it in this bug report.

Note You need to log in before you can comment on or make changes to this bug.