Hi Mark,
On 2016-06-23, Mark Wielaard <[email protected]> wrote:
>> When getting section headers it is assumed that the first section
>> is on the first section list. However, it is possible that the
>> first section list only contains the zeroth section, in which
>> case either illegal memory access occurs or elf_nextscn()
>> erroneously returns NULL.
>>
>> With this patch, checks are added to avoid the illegal memory
>> access and (if available) the second section list is looked at
>> to find the first section.
>
> Both changes to updatenull and nextscn do make sense to me.
>
> I assume this wasn't just theoretical? I didn't immediately see how
> this situation occurs. Do you happen to have an example/testcase?
The situation occurs when adding sections to an existing ELF file that
has none. You can see that in:
libelf/elf_begin.c:file_read_elf()
When an ELF file is opened with ELF_C_RDWR or ELF_C_RDWR_MMAP, scnmax is
set to 1. That leads to the first section being placed on the second
section list when elf_newscn() is called.
Below is a relatively simple program to demonstrate this. This program
adds customized section notes to core files. It is being developed as a
feature for the minicoredumper project.
John Ogness
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <libelf.h>
#include <gelf.h>
#include <sys/types.h>
#include <sys/stat.h>
static int add_shstrtab_section(Elf *e, GElf_Off offset, GElf_Word *size)
{
GElf_Shdr shdr;
Elf_Data *data;
Elf_Scn *scn;
scn = elf_newscn(e);
if (!scn)
return -1;
data = elf_newdata(scn);
if (!data)
return -1;
data->d_align = 1;
data->d_off = 0;
data->d_buf = "\0.debug\0.shstrtab";
data->d_type = ELF_T_BYTE;
data->d_size = 18;
data->d_version = EV_CURRENT;
if (gelf_getshdr(scn, &shdr) == NULL)
return -1;
shdr.sh_name = 8;
shdr.sh_type = SHT_STRTAB;
shdr.sh_flags = SHF_STRINGS;
shdr.sh_entsize = 0;
shdr.sh_size = data->d_size;
shdr.sh_offset = offset;
shdr.sh_addralign = 1;
gelf_update_shdr(scn, &shdr);
*size = shdr.sh_size;
return 0;
}
static int add_debug_section(Elf *e, GElf_Off offset, GElf_Word size)
{
GElf_Shdr shdr;
Elf_Scn *scn;
scn = elf_newscn(e);
if (!scn)
return -1;
if (gelf_getshdr(scn, &shdr) == NULL)
return -1;
shdr.sh_name = 1;
shdr.sh_type = SHT_PROGBITS;
shdr.sh_flags = 0;
shdr.sh_entsize = 0;
shdr.sh_offset = offset;
shdr.sh_size = size;
shdr.sh_addralign = 1;
gelf_update_shdr(scn, &shdr);
return 0;
}
int main(int argc, const char *argv[])
{
size_t last_offset;
GElf_Ehdr ehdr;
GElf_Word size;
struct stat sb;
int ret;
Elf *e;
int fd;
if (elf_version(EV_CURRENT) == EV_NONE)
return 1;
fd = open(argv[1], O_RDWR);
if (fd < 0)
return 1;
e = elf_begin(fd, ELF_C_RDWR, NULL);
if (!e)
return 1;
elf_flagelf(e, ELF_C_SET, ELF_F_LAYOUT);
if (gelf_getehdr(e, &ehdr) == NULL)
return 1;
/* use file size as last offset */
if (fstat(fd, &sb) != 0)
return 1;
last_offset = sb.st_size;
/* cover everything after the elf header to ensure
* that there are no gaps for libelf to fill */
if (add_debug_section(e, ehdr.e_ehsize,
last_offset - ehdr.e_ehsize) != 0) {
return 1;
}
if (add_shstrtab_section(e, last_offset, &size) != 0)
return 1;
last_offset += size;
ehdr.e_shoff = last_offset;
ehdr.e_shstrndx = 2;
gelf_update_ehdr(e, &ehdr);
elf_flagelf(e, ELF_C_SET, ELF_F_DIRTY);
ret = elf_update(e, ELF_C_WRITE);
if (ret < 0)
return 1;
elf_end(e);
close(fd);
return 0;
}