Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 70 additions & 14 deletions src/install/dracut-install.c
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,28 @@ static char *expand_runpath(const char *input, const char *src, const Elf64_Ehdr
that is expanded using the C compiler rather than the preprocessor. */
#define ELF_BYTESWAP(SIZE, value) (ehdr->e_ident[EI_DATA] == ELFDATA2MSB ? be##SIZE##toh(value) : le##SIZE##toh(value))

/* Safely look up a section name in a bounds-checked section header string
table. Returns NULL if the name offset is out of bounds or the name is
not NUL-terminated within the table. */
static const char *elf_sect_name(const char *shstrtab, size_t shstrtab_len, uint32_t name_off)
{
if (name_off >= shstrtab_len || !memchr(shstrtab + name_off, '\0', shstrtab_len - name_off))
return NULL;

return shstrtab + name_off;
}

/* Return a pointer to the NUL-terminated string at the given offset within
the mapped file, or NULL if the offset is out of bounds or the string
would not be NUL-terminated within the map. */
static const char *elf_map_string(const char *map, size_t src_len, size_t offset)
{
if (offset >= src_len || !memchr(map + offset, '\0', src_len - offset))
return NULL;

return map + offset;
}

/* Get a pointer to the ELF header map's section header string table, where B is
64 or 32 bit. Sanity checks the ELF structure to avoid crashes. */
#define PARSE_ELF_START(B, map) \
Expand All @@ -863,9 +885,24 @@ static char *expand_runpath(const char *input, const char *src, const Elf64_Ehdr
ELF_BYTESWAP(B, ehdr->e_shoff) > src_len || \
ELF_BYTESWAP(16, ehdr->e_shstrndx) >= ELF_BYTESWAP(16, ehdr->e_shnum)) \
break; \
\
/* The whole section header table must fit into the mapped file, \
otherwise indexing into it could read past the mapping. */ \
if ((size_t)ELF_BYTESWAP(16, ehdr->e_shnum) > \
(src_len - (size_t)ELF_BYTESWAP(B, ehdr->e_shoff)) / sizeof(Elf##B##_Shdr)) \
break; \
\
Elf##B##_Shdr *shdr = (Elf##B##_Shdr *)((char *)map + ELF_BYTESWAP(B, ehdr->e_shoff)); \
const char *shstrtab = (char *)map + ELF_BYTESWAP(B, shdr[ELF_BYTESWAP(16, ehdr->e_shstrndx)].sh_offset);
Elf##B##_Shdr *shstrshdr = &shdr[ELF_BYTESWAP(16, ehdr->e_shstrndx)]; \
\
/* The section header string table must fit into the mapped file, \
otherwise comparing section names could read past the mapping. */ \
size_t shstrtab_len = ELF_BYTESWAP(B, shstrshdr->sh_size); \
if (ELF_BYTESWAP(B, shstrshdr->sh_offset) > src_len || \
shstrtab_len > src_len - (size_t)ELF_BYTESWAP(B, shstrshdr->sh_offset)) \
break; \
\
const char *shstrtab = (char *)map + ELF_BYTESWAP(B, shstrshdr->sh_offset);

/* Expand the R(UN)PATH of the ELF header map and search it for a library
matching soname and match64/match32. map must point to the same header as
Expand All @@ -875,17 +912,29 @@ static char *expand_runpath(const char *input, const char *src, const Elf64_Ehdr
bool seen_runpath = false; \
\
for (size_t i = 0; i < ELF_BYTESWAP(16, ehdr->e_shnum); i++) { \
if (strcmp(&shstrtab[ELF_BYTESWAP(32, shdr[i].sh_name)], ".dynamic") != 0) \
const char *sect_name = elf_sect_name(shstrtab, shstrtab_len, ELF_BYTESWAP(32, shdr[i].sh_name)); \
if (!sect_name || strcmp(sect_name, ".dynamic") != 0) \
continue; \
\
Elf##B##_Dyn *dyn = (Elf##B##_Dyn *)((char *)map + ELF_BYTESWAP(B, shdr[i].sh_offset)); \
if ((char *)dyn < (char *)map || (char *)dyn > (char *)map + src_len) \

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think void * rather than char * here and below would be equivalent but less confusing?

break; \
\
for (Elf##B##_Dyn *d = dyn; ELF_BYTESWAP(32, d->d_tag) != DT_NULL; d++) { \
if ((char *)d < (char *)map || (char *)d + sizeof(Elf##B##_Dyn) > (char *)map + src_len) \
break; \
if (ELF_BYTESWAP(B, d->d_tag) == DT_RUNPATH) \
seen_runpath = true; /* RUNPATH has precedence over RPATH. */ \
else if (seen_runpath || ELF_BYTESWAP(B, d->d_tag) != DT_RPATH) \
continue; \
\
char *runpath = (char *)map + ELF_BYTESWAP(B, shdr[ELF_BYTESWAP(32, shdr[i].sh_link)].sh_offset) + ELF_BYTESWAP(B, d->d_un.d_val); \
if (ELF_BYTESWAP(32, shdr[i].sh_link) >= ELF_BYTESWAP(16, ehdr->e_shnum)) \
break; \
\
const char *runpath = elf_map_string((const char *)map, src_len, ELF_BYTESWAP(B, shdr[ELF_BYTESWAP(32, shdr[i].sh_link)].sh_offset) + ELF_BYTESWAP(B, d->d_un.d_val)); \
if (!runpath) \
break; \
\
_cleanup_free_ char *expanded = expand_runpath(runpath, src, match64); \
if (!expanded) \
continue; \
Expand Down Expand Up @@ -1025,7 +1074,8 @@ static void resolve_deps_dlopen_parse_json(Hashmap *pdeps, Hashmap *deps, const
for (size_t i = 0; !soname && i < ELF_BYTESWAP(16, ehdr->e_shnum); i++) { \
if ((char*)&shdr[i] < (char*)map || (char*)&shdr[i] + sizeof(Elf##B##_Shdr) > (char*)map + src_len) \
break; \
if (strcmp(&shstrtab[ELF_BYTESWAP(32, shdr[i].sh_name)], ".dynamic") != 0) \
const char *sect_name = elf_sect_name(shstrtab, shstrtab_len, ELF_BYTESWAP(32, shdr[i].sh_name)); \
if (!sect_name || strcmp(sect_name, ".dynamic") != 0) \
continue; \
\
Elf##B##_Dyn *dyn = (Elf##B##_Dyn *)((char *)map + ELF_BYTESWAP(B, shdr[i].sh_offset)); \
Expand All @@ -1038,18 +1088,20 @@ static void resolve_deps_dlopen_parse_json(Hashmap *pdeps, Hashmap *deps, const
if (ELF_BYTESWAP(B, d->d_tag) != DT_SONAME) \
continue; \
\
soname = (char *)map + ELF_BYTESWAP(B, shdr[ELF_BYTESWAP(32, shdr[i].sh_link)].sh_offset) + ELF_BYTESWAP(B, d->d_un.d_val); \
if ((char *)soname < (char *)map || (char *)soname > (char *)map + src_len) { \
soname = NULL; \
if (ELF_BYTESWAP(32, shdr[i].sh_link) >= ELF_BYTESWAP(16, ehdr->e_shnum)) \
break; \
\
soname = elf_map_string((const char *)map, src_len, ELF_BYTESWAP(B, shdr[ELF_BYTESWAP(32, shdr[i].sh_link)].sh_offset) + ELF_BYTESWAP(B, d->d_un.d_val)); \
if (!soname) \
break; \
} \
} \
} \
\
for (size_t i = 0; i < ELF_BYTESWAP(16, ehdr->e_shnum); i++) { \
if ((char*)shdr + i * sizeof(Elf##B##_Shdr) > (char*)map + src_len) \
break; \
if (strcmp(&shstrtab[ELF_BYTESWAP(32, shdr[i].sh_name)], ".note.dlopen") != 0) \
const char *sect_name = elf_sect_name(shstrtab, shstrtab_len, ELF_BYTESWAP(32, shdr[i].sh_name)); \
if (!sect_name || strcmp(sect_name, ".note.dlopen") != 0) \
continue; \
\
const char *note_offset = (char *)map + ELF_BYTESWAP(B, shdr[i].sh_offset); \
Expand Down Expand Up @@ -1094,8 +1146,8 @@ static void resolve_deps_dlopen_parse_json(Hashmap *pdeps, Hashmap *deps, const
if (ELF_BYTESWAP(32, phdr->p_type) != PT_INTERP) \
continue; \
\
const char *interpreter = (const char *)map + ELF_BYTESWAP(B, phdr->p_offset); \
if (interpreter < (char *)map || interpreter > (char *)map + src_len) \
const char *interpreter = elf_map_string((const char *)map, src_len, ELF_BYTESWAP(B, phdr->p_offset)); \
if (!interpreter) \
break; \
if (hashmap_get(pdeps, interpreter)) \
continue; \
Expand All @@ -1112,7 +1164,8 @@ static void resolve_deps_dlopen_parse_json(Hashmap *pdeps, Hashmap *deps, const
for (size_t i = 0; i < ELF_BYTESWAP(16, ehdr->e_shnum); i++) { \
if ((char*)&shdr[i] < (char*)map || (char*)&shdr[i] + sizeof(Elf##B##_Shdr) > (char*)map + src_len) \
break; \
if (strcmp(&shstrtab[ELF_BYTESWAP(32, shdr[i].sh_name)], ".dynamic") != 0) \
const char *sect_name = elf_sect_name(shstrtab, shstrtab_len, ELF_BYTESWAP(32, shdr[i].sh_name)); \
if (!sect_name || strcmp(sect_name, ".dynamic") != 0) \
continue; \
\
Elf##B##_Dyn *dyn = (Elf##B##_Dyn *)((char *)map + ELF_BYTESWAP(B, shdr[i].sh_offset)); \
Expand All @@ -1125,8 +1178,11 @@ static void resolve_deps_dlopen_parse_json(Hashmap *pdeps, Hashmap *deps, const
if (ELF_BYTESWAP(B, d->d_tag) != DT_NEEDED) \
continue; \
\
const char *soname = (char *)map + ELF_BYTESWAP(B, shdr[ELF_BYTESWAP(32, shdr[i].sh_link)].sh_offset) + ELF_BYTESWAP(B, d->d_un.d_val); \
if ((char *)soname < (char *)map || (char *)soname > (char *)map + src_len) \
if (ELF_BYTESWAP(32, shdr[i].sh_link) >= ELF_BYTESWAP(16, ehdr->e_shnum)) \
break; \
\
const char *soname = elf_map_string((const char *)map, src_len, ELF_BYTESWAP(B, shdr[ELF_BYTESWAP(32, shdr[i].sh_link)].sh_offset) + ELF_BYTESWAP(B, d->d_un.d_val)); \
if (!soname) \
break; \
if (hashmap_get(pdeps, soname)) \
continue; \
Expand Down
Loading