Bug 1273250

Summary: Adding a new section to an ELF file SIGSEGVs and core dumps on RHEL 7 but has no issues on RHEL 6.5. The elfutils or libelf behavior are different between RHEL7 and RHEL 6.5.
Product: Red Hat Enterprise Linux 7 Reporter: Ron <monthero.ronald>
Component: elfutilsAssignee: Mark Wielaard <mjw>
Status: CLOSED NOTABUG QA Contact: qe-baseos-tools-bugs
Severity: high Docs Contact:
Priority: unspecified    
Version: 7.0CC: adnanbajwa88, mbenitez, mjw, monthero.ronald
Target Milestone: rc   
Target Release: ---   
Hardware: x86_64   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2015-10-20 12:03:33 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Attachments:
Description Flags
1)My sample program to create a new section in an elf binary. Attaching my sample program eadds.c with this email ) none

Description Ron 2015-10-20 04:06:42 UTC
Description of problem:

Architecture: x86_64

I am using libelf APIs to add a new section to an ELF binary. 
The libelf behavior seems to exhibit different behavior between RHEL 6.5 and RHEL 7.0
I have a sample program that I compiled to add a new section to an ELF file which I used a reference on both systems. 


It works fine on a RHEL 6.5 system and but core dumps on RHEL 7 with a sigsegv and after the  arch_prctl( ) on a  RHEL 7.0 system 
The new section in the ELF gets created even on RHEL 7 but it sigsegvs and dumps core. 
The only difference is between the versions of libelf and binutils used. 
I encounter this failure, even with the latest version of elfutils and binutils available for RHEL 7 


Version-Release number of selected component (if applicable):

oN RHEL 7.0 

## rpm -qa | grep -i elfutils
elfutils-libelf-devel-0.160-1.el7.x86_64
elfutils-libelf-0.160-1.el7.x86_64
elfutils-0.160-1.el7.x86_64
elfutils-libs-0.160-1.el7.x86_64
##

## rpm -qa | grep -i binutils
binutils-devel-2.23.52.0.1-30.el7.x86_64
binutils-2.23.52.0.1-30.el7.x86_64
## 

ON RHEL 6.5 (Runs Successful )

## rpm -qa | grep -i elfutils
elfutils-devel-0.161-3.el6.x86_64
elfutils-libelf-0.161-3.el6.x86_64
elfutils-libs-0.161-3.el6.x86_64
elfutils-0.161-3.el6.x86_64
elfutils-libelf-devel-0.161-3.el6.x86_64
##

## rpm -qa | grep -i binutils
binutils-devel-2.20.51.0.2-5.36.el6.x86_64
binutils-2.20.51.0.2-5.36.el6.x86_64
## 



How reproducible:


Steps to Reproduce:

Procedure to reproduce:

1)	A sample program to create a new section in an elf binary. 
( Attaching my sample program eadds.c with this email ) 

Compile this program 

gcc eadds.c –o eadds –lelf  

2)	Compile a test elf binary of a sample hello world program.
Use readelf –S hello-world  ( To know the number of sections in the ELF binary before adding a new section) 

3)	Create a new section in this hello world binary and verify the same using readelf utility

./eadds hello-world

readelf –S hello-world                ( Usually the last section added is the new section )

4)	Now run the hello-world binary – Expected Output: It should run successfully and display the text (Hello WOrld) though its embedded with a new section in the elf. 


Actual results:

[root@localhost test-elf]# strace ./a.out
execve("./a.out", ["./a.out"], [/* 31 vars */]) = 0
brk(0)                                  = 0x699000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2daf534000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2daf533000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2daf532000
arch_prctl(ARCH_SET_FS, 0x7f2daf533680) = 0
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x8} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)
[root@localhost test-elf]#


Expected results:

After running ./eadds hello-world 

It should add a new section to the ELF you can valiadte it using readelf -S hello-world. Typically the new section gets added at the end towards the last section.

And when you run the modified hello-world ELF binary it should execute as usual and display the text. 


Example: 

[root@rhel65 wli-elf-temp]# readelf -S a.out
There are 31 section headers, starting at offset 0x11d8:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400200  00000200
       000000000000001c  0000000000000000   A       0     0     1
...
...
...
...
  [29] .strtab           STRTAB           0000000000000000  00000fe0
       00000000000001f0  0000000000000000           0     0     1
  [30] ab                NOTE             0000000000000000  000011d0
       0000000000000008  0000000000000000   A       0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
[root@rhel65 wli-elf-temp]#

In the above successful case (RHEL 6.5), the new section created is section 30.


Run the modified ELF:

[root@rhel65 wli-elf-temp]# ./a.out

 Hello Linux Users !!

[root@rhel65 wli-elf-temp]#



Additional info:

The program to add anew section is attached in this report and is named eadds.c.
But the hello world sample program needs to be created, you can create a sample hello world program as the ELF to be modified by eadds

Comment 1 Ron 2015-10-20 04:08:54 UTC
Created attachment 1084583 [details]
1)My sample program to create a new section in an elf binary.   Attaching my sample program eadds.c with this email )

My sample program to create a new section in an elf binary. 
( Attaching my sample program eadds.c with this email )

Comment 3 Mark Wielaard 2015-10-20 12:03:33 UTC
When adding content to an existing executable ELF file you should keep the phdrs that tell where the contents of the segments are read during runtime in tact. That means either updating the phdrs after adjusting the sections (which you probably want to do anyway since you are adding an allocated sections) or (if the section you add shouldn't be allocated) you need to keep the layout of the sections exactly as they were (so the phdr offsets match).

This means you should at least use elf_flagelf (elf_ref, ELF_C_SET, ELF_F_LAYOUT); (this and the other flags should be set before you call elf_update for the fist time). In that case you are responsible for setting the section sh_offset, sh_size and sh_addr_align fields. And for updating the ehdr e_shoff field to tell libelf where the section headers should be written (normally at the end of the file).

In particular for your example you probably want to do something like:

  /* Keep track of last section offset in the file.  */
  GElf_Off last_offset = 0;
  Elf_Scn *scn = NULL;
  while ((scn = elf_nextscn (elf_ref, scn)) != NULL)
    {
      GElf_Shdr shdr;
      if (gelf_getshdr (scn, &shdr) == NULL)
        {
          printf ("gelf_getshdr failed: %s\n", elf_errmsg (-1));
          close (fd);
          return -1;
        }
      if (shdr.sh_type != SHT_NOBITS
          && last_offset < shdr.sh_offset + shdr.sh_size)
        last_offset = shdr.sh_offset + shdr.sh_size;
    }

  /* ... */
  signature_scn_header->sh_offset = last_offset;
  signature_scn_header->sh_size = sizeof(sample_data);
  signature_scn_header->sh_addralign = 1;

  ehdr->e_shoff = last_offset + sizeof(sample_data);

  elf_flagelf (elf_ref, ELF_C_SET, ELF_F_LAYOUT);

  /* ... */
  elf_update()

Please reopen this bug if the above didn't help you and you still believe there is a bug in elfutils libelf.

Comment 4 Ron 2015-11-09 12:30:58 UTC
Mark Thanks for the piece of info. 

I tried it a few times but the program core dumps (even on RHEL 6.5) when I use ELF_F_LAYOUT flag in elf_flagelf() instead of ELF_F_DIRTY flag.

Probably I might be missing something when I tried to stitch the code with your code snippets ( and I am not too familiar with ELF but just just aware of a few ELF library calls to add a section in an ELF file) 

And also the difference in behavior between RHEL 6.5 and RHEL 7.0 
concerns me a little. Even with my original sample program - It works fine on a RHEL 6.5 but sigsegv on RHEL 7 ( On RHEL 7 creation of section in ELF is successful with my initial sample program, but core dumps segv when its executed) 

It would be of much help if you have steps to stitch your code with my sample program so that it will enable me to readily test and report to you.

I can test it on both RHEL 6.5 and RHEL 7.0 and get back.

Thanks and Regards

-Ron

Comment 5 Mark Wielaard 2015-11-16 10:25:02 UTC
(In reply to Ron from comment #4)
> I tried it a few times but the program core dumps (even on RHEL 6.5) when I
> use ELF_F_LAYOUT flag in elf_flagelf() instead of ELF_F_DIRTY flag.

You have to at least call elf_flagelf before the first elf_update.
And if you do you are responsible for setting the the section sh_offset, sh_size and sh_addr_align fields and for updating the ehdr e_shoff field.

> And also the difference in behavior between RHEL 6.5 and RHEL 7.0 
> concerns me a little. Even with my original sample program - It works fine
> on a RHEL 6.5 but sigsegv on RHEL 7 ( On RHEL 7 creation of section in ELF
> is successful with my initial sample program, but core dumps segv when its
> executed) 

I expect that it working on rhel 6.5 is just by accident.
Note that the program you attached doesn't compile:

$ gcc -g -Wall -o eadds -lelf eadds.c
eadds.c: In function ‘main’:
eadds.c:94:1: error: expected declaration or statement at end of input
 }
 ^
eadds.c:28:21: warning: unused variable ‘secname’ [-Wunused-variable]
         char *fnm, *secname = ".HP.wli";
                     ^
eadds.c:25:18: warning: unused variable ‘elf_scn’ [-Wunused-variable]
         Elf_Scn *elf_scn, *sig_scn;
                  ^
eadds.c:24:21: warning: unused variable ‘ehdr’ [-Wunused-variable]
         Elf64_Ehdr *ehdr;
                     ^
eadds.c:94:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^

Comment 6 Ron 2015-11-24 17:23:05 UTC
Thanks for the response.

Oh well I guess when u copy from the bug report the program gets mangle because of the "//" style comments and also commenting unused variables 


Here is the same program with /* */ style comments (instead of //) and this should compile and execute successfully on RHEL 6.5 and fail on RHEL 7


/* PROGRAM BELOW */


/* Program to add  a new section to an ELF binary 

 compile this program as gcc eadds.c -o eadds -lelf
 Usage :  	./eadds test-elf  
 where test-elf is your sample ELF binary or a hello world  elf binary 
*/

#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <elf.h>
#include <libelf.h>
#include <sys/resource.h>
int main(int argc, char **argv)
{
        int fd;
        Elf *elf_ref = NULL;
/*        Elf64_Ehdr *ehdr; */
/*        Elf_Scn *elf_scn; */
        Elf_Scn *sig_scn;
        Elf_Data *elf_data;
        Elf64_Shdr *signature_scn_header;
        char *fnm;
/*      char  *secname = ".HP.wli"; */
        unsigned char sample_data[] = {0xca, 0xfe, 0xba, 0xbe, 0xca, 0xfe, 0xba, 0xbe};

        if(argc != 2){
                printf("\n Usage: provide a command line input file  \n");
                exit(-1);
        }
        fnm = argv[1];
        fd = open(fnm,O_RDWR, NULL);

        /* Protect from using a lower ELF version and initialize ELF library */
        if (elf_version(EV_CURRENT) == EV_NONE) {
                printf("ELF library init failed: %s\n", elf_errmsg(-1));
                close(fd);
                return -1;
        }

        elf_ref = elf_begin(fd, ELF_C_RDWR, NULL);

        if (elf_kind(elf_ref) != ELF_K_ELF) {
                printf("Program is not an ELF binary\n");
                close(fd);
                return -2;
        }

        sig_scn = elf_newscn(elf_ref);

        elf_data = elf_newdata(sig_scn);
        elf_data->d_align = 1;
        elf_data->d_off = 0LL ;
        elf_data->d_buf = sample_data ;
        elf_data->d_type = ELF_T_BYTE ;
        elf_data->d_size = sizeof(sample_data);
        elf_data->d_version = EV_CURRENT ;

        signature_scn_header = elf64_getshdr(sig_scn);
        if (signature_scn_header == NULL)
        {
                printf("ELF elf32_getshdr failed");
                return -2;
        }
/*        signature_scn_header->sh_name = 1; */
        signature_scn_header->sh_name = 16; 
        signature_scn_header->sh_type = SHT_NOTE;
/*        signature_scn_header->sh_type = SHT_PROGBITS; */
        signature_scn_header->sh_flags = SHF_ALLOC;
        signature_scn_header->sh_entsize = 0;

        if (elf_update(elf_ref , ELF_C_NULL ) < 0) {
                printf("ELF update failed: %s", elf_errmsg (-1));
                return -3;
        }

        (void) elf_flagshdr(sig_scn, ELF_C_SET, ELF_F_DIRTY);
        (void) elf_flagscn(sig_scn, ELF_C_SET, ELF_F_DIRTY);
        (void) elf_flagdata(elf_data, ELF_C_SET, ELF_F_DIRTY);
        (void) elf_flagehdr(elf_ref, ELF_C_SET, ELF_F_DIRTY);

        (void) elf_flagelf(elf_ref, ELF_C_SET, ELF_F_DIRTY);

        if (elf_update(elf_ref , ELF_C_WRITE ) < 0) {
                printf("ELF update failed: %s", elf_errmsg (-1));
                return -4;
        }

        elf_end(elf_ref);

        close(fd);
        return 0;
}




readelf -S testx

..

  [28] .symtab           SYMTAB           0000000000000000  000009e0
       0000000000000600  0000000000000018          29    46     8
  [29] .strtab           STRTAB           0000000000000000  00000fe0
       00000000000001f0  0000000000000000           0     0     1
  [30]                   NULL             0000000000000000  1000300000000
       0000000000400200  000000000040021c           0     0     562962838323200
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
[root@srp-linux-7-rhel6 wli-elf]#

Section 30 is the new section added.



Successful program Execution after adding a new section to the elf binary.

[root]# ./testx

 Hello Linux Users
[root@srp-linux-7-rhel6 wli-elf]#

Comment 7 Ron 2015-11-24 17:23:43 UTC
Thanks for the response.

Oh well I guess when u copy from the bug report the program gets mangle because of the "//" style comments and also commenting unused variables 


Here is the same program with /* */ style comments (instead of //) and this should compile and execute successfully on RHEL 6.5 and fail on RHEL 7


/* PROGRAM BELOW */


/* Program to add  a new section to an ELF binary 

 compile this program as gcc eadds.c -o eadds -lelf
 Usage :  	./eadds test-elf  
 where test-elf is your sample ELF binary or a hello world  elf binary 
*/

#include <stdio.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <elf.h>
#include <libelf.h>
#include <sys/resource.h>
int main(int argc, char **argv)
{
        int fd;
        Elf *elf_ref = NULL;
/*        Elf64_Ehdr *ehdr; */
/*        Elf_Scn *elf_scn; */
        Elf_Scn *sig_scn;
        Elf_Data *elf_data;
        Elf64_Shdr *signature_scn_header;
        char *fnm;
/*      char  *secname = ".HP.wli"; */
        unsigned char sample_data[] = {0xca, 0xfe, 0xba, 0xbe, 0xca, 0xfe, 0xba, 0xbe};

        if(argc != 2){
                printf("\n Usage: provide a command line input file  \n");
                exit(-1);
        }
        fnm = argv[1];
        fd = open(fnm,O_RDWR, NULL);

        /* Protect from using a lower ELF version and initialize ELF library */
        if (elf_version(EV_CURRENT) == EV_NONE) {
                printf("ELF library init failed: %s\n", elf_errmsg(-1));
                close(fd);
                return -1;
        }

        elf_ref = elf_begin(fd, ELF_C_RDWR, NULL);

        if (elf_kind(elf_ref) != ELF_K_ELF) {
                printf("Program is not an ELF binary\n");
                close(fd);
                return -2;
        }

        sig_scn = elf_newscn(elf_ref);

        elf_data = elf_newdata(sig_scn);
        elf_data->d_align = 1;
        elf_data->d_off = 0LL ;
        elf_data->d_buf = sample_data ;
        elf_data->d_type = ELF_T_BYTE ;
        elf_data->d_size = sizeof(sample_data);
        elf_data->d_version = EV_CURRENT ;

        signature_scn_header = elf64_getshdr(sig_scn);
        if (signature_scn_header == NULL)
        {
                printf("ELF elf32_getshdr failed");
                return -2;
        }
/*        signature_scn_header->sh_name = 1; */
        signature_scn_header->sh_name = 16; 
        signature_scn_header->sh_type = SHT_NOTE;
/*        signature_scn_header->sh_type = SHT_PROGBITS; */
        signature_scn_header->sh_flags = SHF_ALLOC;
        signature_scn_header->sh_entsize = 0;

        if (elf_update(elf_ref , ELF_C_NULL ) < 0) {
                printf("ELF update failed: %s", elf_errmsg (-1));
                return -3;
        }

        (void) elf_flagshdr(sig_scn, ELF_C_SET, ELF_F_DIRTY);
        (void) elf_flagscn(sig_scn, ELF_C_SET, ELF_F_DIRTY);
        (void) elf_flagdata(elf_data, ELF_C_SET, ELF_F_DIRTY);
        (void) elf_flagehdr(elf_ref, ELF_C_SET, ELF_F_DIRTY);

        (void) elf_flagelf(elf_ref, ELF_C_SET, ELF_F_DIRTY);

        if (elf_update(elf_ref , ELF_C_WRITE ) < 0) {
                printf("ELF update failed: %s", elf_errmsg (-1));
                return -4;
        }

        elf_end(elf_ref);

        close(fd);
        return 0;
}




readelf -S testx

..

  [28] .symtab           SYMTAB           0000000000000000  000009e0
       0000000000000600  0000000000000018          29    46     8
  [29] .strtab           STRTAB           0000000000000000  00000fe0
       00000000000001f0  0000000000000000           0     0     1
  [30]                   NULL             0000000000000000  1000300000000
       0000000000400200  000000000040021c           0     0     562962838323200
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)
[root@srp-linux-7-rhel6 wli-elf]#

Section 30 is the new section added.



Successful program Execution after adding a new section to the elf binary.

[root]# ./testx

 Hello Linux Users
[root@srp-linux-7-rhel6 wli-elf]#

Comment 8 adnan bajwa 2018-06-07 05:36:24 UTC
(In reply to Ron from comment #1)
> Created attachment 1084583 [details]
> 1)My sample program to create a new section in an elf binary.   Attaching my
> sample program eadds.c with this email )
> 
> My sample program to create a new section in an elf binary. 
> ( Attaching my sample program eadds.c with this email 
  


i compile your program according top your instruction but at the end i received 
"program is not in elf binary"