mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-22 17:23:06 +00:00
libdrgn: handle reading data from SHT_NOBITS sections
Peilin Ye reported a couple of related crashes in drgn caused by Linux kernel modules which had been processed with objcopy --only-keep-debug (although he notes that since binutils-gdb commit 8c803a2dd7d3 ("elf_backend_section_flags and _bfd_elf_init_private_section_data") (in binutils v2.35), objcopy --only-keep-debug doesn't seem to work for kernel modules). If given an SHT_NOBITS section, elf_getdata() returns an Elf_Data with d_buf = NULL and d_size set to the size in the section header, which is often non-zero. There are a few places where this can cause us to dereference a NULL pointer: * In relocate_elf_sections() for the relocated section data. * In relocate_elf_sections() for the symbol table section data. * In get_kernel_module_name_from_modinfo(). * In get_kernel_module_name_from_this_module(). Fix it by checking the section type or directly checking Elf_Data::d_buf everywhere that could potentially get an SHT_NOBITS section. This is based on a PR from Peilin Ye. Closes #145. Reported-by: Peilin Ye <peilin.ye@bytedance.com> Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
parent
8e8e3a4f57
commit
929b7de266
@ -1628,17 +1628,18 @@ static struct drgn_error *relocate_elf_file(Elf *elf)
|
||||
|
||||
Elf_Scn *reloc_scn = NULL;
|
||||
while ((reloc_scn = elf_nextscn(elf, reloc_scn))) {
|
||||
GElf_Shdr *shdr, shdr_mem;
|
||||
shdr = gelf_getshdr(reloc_scn, &shdr_mem);
|
||||
if (!shdr) {
|
||||
GElf_Shdr *reloc_shdr, reloc_shdr_mem;
|
||||
reloc_shdr = gelf_getshdr(reloc_scn, &reloc_shdr_mem);
|
||||
if (!reloc_shdr) {
|
||||
err = drgn_error_libelf();
|
||||
goto out;
|
||||
}
|
||||
/* We don't support any architectures that use SHT_REL yet. */
|
||||
if (shdr->sh_type != SHT_RELA)
|
||||
if (reloc_shdr->sh_type != SHT_RELA)
|
||||
continue;
|
||||
|
||||
const char *scnname = elf_strptr(elf, shstrndx, shdr->sh_name);
|
||||
const char *scnname = elf_strptr(elf, shstrndx,
|
||||
reloc_shdr->sh_name);
|
||||
if (!scnname) {
|
||||
err = drgn_error_libelf();
|
||||
goto out;
|
||||
@ -1646,17 +1647,36 @@ static struct drgn_error *relocate_elf_file(Elf *elf)
|
||||
|
||||
if (strstartswith(scnname, ".rela.debug_") ||
|
||||
strstartswith(scnname, ".rela.orc_")) {
|
||||
Elf_Scn *scn = elf_getscn(elf, shdr->sh_info);
|
||||
Elf_Scn *scn = elf_getscn(elf, reloc_shdr->sh_info);
|
||||
if (!scn) {
|
||||
err = drgn_error_libelf();
|
||||
goto out;
|
||||
}
|
||||
GElf_Shdr *shdr, shdr_mem;
|
||||
shdr = gelf_getshdr(scn, &shdr_mem);
|
||||
if (!shdr) {
|
||||
err = drgn_error_libelf();
|
||||
goto out;
|
||||
}
|
||||
if (shdr->sh_type == SHT_NOBITS)
|
||||
continue;
|
||||
|
||||
Elf_Scn *symtab_scn = elf_getscn(elf, shdr->sh_link);
|
||||
Elf_Scn *symtab_scn = elf_getscn(elf,
|
||||
reloc_shdr->sh_link);
|
||||
if (!symtab_scn) {
|
||||
err = drgn_error_libelf();
|
||||
goto out;
|
||||
}
|
||||
shdr = gelf_getshdr(symtab_scn, &shdr_mem);
|
||||
if (!shdr) {
|
||||
err = drgn_error_libelf();
|
||||
goto out;
|
||||
}
|
||||
if (shdr->sh_type == SHT_NOBITS) {
|
||||
err = drgn_error_create(DRGN_ERROR_OTHER,
|
||||
"relocation symbol table has no data");
|
||||
goto out;
|
||||
}
|
||||
|
||||
Elf_Data *data, *reloc_data, *symtab_data;
|
||||
if ((err = read_elf_section(scn, &data)) ||
|
||||
@ -1681,8 +1701,8 @@ static struct drgn_error *relocate_elf_file(Elf *elf)
|
||||
* Mark the relocation section as empty so that libdwfl
|
||||
* doesn't try to apply it again.
|
||||
*/
|
||||
shdr->sh_size = 0;
|
||||
if (!gelf_update_shdr(reloc_scn, shdr)) {
|
||||
reloc_shdr->sh_size = 0;
|
||||
if (!gelf_update_shdr(reloc_scn, reloc_shdr)) {
|
||||
err = drgn_error_libelf();
|
||||
goto out;
|
||||
}
|
||||
|
@ -332,6 +332,10 @@ struct drgn_error *open_elf_file(const char *path, int *fd_ret, Elf **elf_ret);
|
||||
struct drgn_error *find_elf_file(char **path_ret, int *fd_ret, Elf **elf_ret,
|
||||
const char * const *path_formats, ...);
|
||||
|
||||
/*
|
||||
* NB: if the section is SHT_NOBITS, this returns an Elf_Data with d_buf = NULL
|
||||
* and d_size >= 0.
|
||||
*/
|
||||
struct drgn_error *read_elf_section(Elf_Scn *scn, Elf_Data **ret);
|
||||
|
||||
struct drgn_error *elf_address_range(Elf *elf, uint64_t bias,
|
||||
|
@ -933,6 +933,10 @@ get_kernel_module_name_from_modinfo(Elf_Scn *modinfo_scn, const char **ret)
|
||||
err = read_elf_section(modinfo_scn, &data);
|
||||
if (err)
|
||||
return err;
|
||||
if (!data->d_buf) {
|
||||
/* Section is either SHT_NOBITS or empty. */
|
||||
goto not_found;
|
||||
}
|
||||
p = data->d_buf;
|
||||
end = p + data->d_size;
|
||||
while (p < end) {
|
||||
@ -946,6 +950,7 @@ get_kernel_module_name_from_modinfo(Elf_Scn *modinfo_scn, const char **ret)
|
||||
p = nul + 1;
|
||||
}
|
||||
}
|
||||
not_found:
|
||||
*ret = NULL;
|
||||
return NULL;
|
||||
}
|
||||
@ -967,7 +972,7 @@ get_kernel_module_name_from_this_module(Elf_Scn *this_module_scn,
|
||||
err = read_elf_section(this_module_scn, &data);
|
||||
if (err)
|
||||
return err;
|
||||
if (name_offset < data->d_size) {
|
||||
if (data->d_buf && name_offset < data->d_size) {
|
||||
p = data->d_buf + name_offset;
|
||||
nul = memchr(p, 0, data->d_size - name_offset);
|
||||
if (nul && nul != p) {
|
||||
|
Loading…
Reference in New Issue
Block a user