From 607e05466d3fef5e3ad90aa200d3bba1950cf982 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Sun, 4 Dec 2005 15:51:06 +0000 Subject: [PATCH] elflint now checks whether program header entries which must be allocated point to loaded segments. Don't bail out on relocations not expected for file type if they are not loaded. --- NEWS | 4 ++ src/ChangeLog | 10 +++++ src/elflint.c | 106 +++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 110 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index 11c9b9e85..6ad05f91f 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +Version 0.118: + +elflint: more tests. + Version 0.117: libdwfl: New function dwfl_module_return_value_location. diff --git a/src/ChangeLog b/src/ChangeLog index 2f7ba0f7e..4c668c231 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,13 @@ +2005-11-28 Ulrich Drepper + + * elflint.c (check_one_reloc): Take additional parameters. Use + them to determine whether relocation is valid in this type of + file. DSOs and executables can contain relocation sections in + unloaded sections which just show the relocations the linker + applied. Adjust all callers. + (check_program_header): Check that PT_PHDR is loaded and that offset + matches the one in the ELF header. + 2005-10-26 Roland McGrath * nm.c (get_var_range): dwarf_getloclist -> dwarf_getlocation. diff --git a/src/elflint.c b/src/elflint.c index cf0f3ca44..227a463cd 100644 --- a/src/elflint.c +++ b/src/elflint.c @@ -1129,8 +1129,9 @@ enum load_state static void -check_one_reloc (Ebl *ebl, int idx, size_t cnt, const GElf_Shdr *symshdr, - Elf_Data *symdata, GElf_Addr r_offset, GElf_Xword r_info, +check_one_reloc (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *relshdr, int idx, + size_t cnt, const GElf_Shdr *symshdr, Elf_Data *symdata, + GElf_Addr r_offset, GElf_Xword r_info, const GElf_Shdr *destshdr, bool reldyn, struct loaded_segment *loaded, enum load_state *statep) { @@ -1139,7 +1140,12 @@ check_one_reloc (Ebl *ebl, int idx, size_t cnt, const GElf_Shdr *symshdr, if (!ebl_reloc_type_check (ebl, GELF_R_TYPE (r_info))) ERROR (gettext ("section [%2d] '%s': relocation %zu: invalid type\n"), idx, section_name (ebl, idx), cnt); - else if (!ebl_reloc_valid_use (ebl, GELF_R_TYPE (r_info))) + else if (((ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) + /* The executable/DSO can contain relocation sections with + all the relocations the linker has applied. Those sections + are marked non-loaded, though. */ + || (relshdr->sh_flags & SHF_ALLOC) != 0) + && !ebl_reloc_valid_use (ebl, GELF_R_TYPE (r_info))) ERROR (gettext ("\ section [%2d] '%s': relocation %zu: relocation type invalid for the file type\n"), idx, section_name (ebl, idx), cnt); @@ -1270,8 +1276,9 @@ section [%2d] '%s': cannot get relocation %zu: %s\n"), continue; } - check_one_reloc (ebl, idx, cnt, symshdr, symdata, rela->r_offset, - rela->r_info, destshdr, reldyn, loaded, &state); + check_one_reloc (ebl, ehdr, shdr, idx, cnt, symshdr, symdata, + rela->r_offset, rela->r_info, destshdr, reldyn, loaded, + &state); } while (loaded != NULL) @@ -1319,8 +1326,9 @@ section [%2d] '%s': cannot get relocation %zu: %s\n"), continue; } - check_one_reloc (ebl, idx, cnt, symshdr, symdata, rel->r_offset, - rel->r_info, destshdr, reldyn, loaded, &state); + check_one_reloc (ebl, ehdr, shdr, idx, cnt, symshdr, symdata, + rel->r_offset, rel->r_info, destshdr, reldyn, loaded, + &state); } while (loaded != NULL) @@ -1337,7 +1345,7 @@ static int ndynamic; static void -check_dynamic (Ebl *ebl, GElf_Shdr *shdr, int idx) +check_dynamic (Ebl *ebl, GElf_Ehdr *ehdr, GElf_Shdr *shdr, int idx) { Elf_Data *data; GElf_Shdr strshdr_mem; @@ -1484,6 +1492,56 @@ section [%2d] '%s': entry %zu: DT_PLTREL value must be DT_REL or DT_RELA\n"), pltreladdr = dyn->d_un.d_ptr; if (dyn->d_tag == DT_PLTRELSZ) pltrelsz = dyn->d_un.d_val; + + /* Check that addresses for entries are in loaded segments. */ + switch (dyn->d_tag) + { + size_t n; + default: + if (dyn->d_tag < DT_ADDRRNGLO || dyn->d_tag > DT_ADDRRNGHI) + /* Value is no pointer. */ + break; + /* FALLTHROUGH */ + + case DT_PLTGOT: + case DT_HASH: + case DT_STRTAB: + case DT_SYMTAB: + case DT_RELA: + case DT_INIT: + case DT_FINI: + case DT_SONAME: + case DT_RPATH: + case DT_SYMBOLIC: + case DT_REL: + case DT_JMPREL: + case DT_INIT_ARRAY: + case DT_FINI_ARRAY: + case DT_RUNPATH: + case DT_VERSYM: + case DT_VERDEF: + case DT_VERNEED: + case DT_AUXILIARY: + case DT_FILTER: + for (n = 0; n < ehdr->e_phnum; ++n) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (ebl->elf, n, &phdr_mem); + if (phdr != NULL && phdr->p_type == PT_LOAD + && phdr->p_vaddr <= dyn->d_un.d_ptr + && phdr->p_vaddr + phdr->p_memsz > dyn->d_un.d_ptr) + break; + } + if (unlikely (n >= ehdr->e_phnum)) + { + char buf[50]; + ERROR (gettext ("\ +section [%2d] '%s': entry %zu: %s value must point into loaded segment\n"), + idx, section_name (ebl, idx), cnt, + ebl_dynamic_tag_name (ebl, dyn->d_tag, buf, + sizeof (buf))); + } + } } for (cnt = 1; cnt < DT_NUM; ++cnt) @@ -2832,7 +2890,7 @@ section [%2zu] '%s': relocatable files cannot have dynamic symbol tables\n"), break; case SHT_DYNAMIC: - check_dynamic (ebl, shdr, cnt); + check_dynamic (ebl, ehdr, shdr, cnt); break; case SHT_SYMTAB_SHNDX: @@ -3177,8 +3235,36 @@ loadable segment GNU_RELRO applies to is executable\n")); if (inner >= ehdr->e_phnum) ERROR (gettext ("\ -GNU_RELRO segment not contained in a loaded segment\n")); +%s segment not contained in a loaded segment\n"), "GNU_RELRO"); + } + } + else if (phdr->p_type == PT_PHDR) + { + /* Check that the region is in a writable segment. */ + int inner; + for (inner = 0; inner < ehdr->e_phnum; ++inner) + { + GElf_Phdr phdr2_mem; + GElf_Phdr *phdr2; + + phdr2 = gelf_getphdr (ebl->elf, inner, &phdr2_mem); + if (phdr2 != NULL + && phdr2->p_type == PT_LOAD + && phdr->p_vaddr >= phdr2->p_vaddr + && (phdr->p_vaddr + phdr->p_memsz + <= phdr2->p_vaddr + phdr2->p_memsz)) + break; } + + if (inner >= ehdr->e_phnum) + ERROR (gettext ("\ +%s segment not contained in a loaded segment\n"), "PHDR"); + + /* Check that offset in segment corresponds to offset in ELF + header. */ + if (phdr->p_offset != ehdr->e_phoff) + ERROR (gettext ("\ +program header offset in ELF header and PHDR entry do not match")); } if (phdr->p_filesz > phdr->p_memsz) -- 2.47.2