Hi Mark, On 2016-06-23, Mark Wielaard <m...@redhat.com> 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; }