ldelf: check dynsymtab and dynstr ranges

Checks the ranges of dynsymtab and dynstr. Also checks that the dynstr
index in section headers isn't out of range. This fixes an error where a
malformed ELF may cause the loader to read data from other ELF or from
the loader itself.

Reviewed-by: Etienne Carriere <etienne.carriere@linaro.org>
Reported-by: Martijn Bogaard <martijn@riscure.com>
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
diff --git a/ldelf/ta_elf.c b/ldelf/ta_elf.c
index d42bf07..04a0158 100644
--- a/ldelf/ta_elf.c
+++ b/ldelf/ta_elf.c
@@ -192,6 +192,22 @@
 	}
 }
 
+static void check_range(struct ta_elf *elf, const char *name, const void *ptr,
+			size_t sz)
+{
+	size_t max_addr = 0;
+
+	if ((vaddr_t)ptr < elf->load_addr)
+		err(TEE_ERROR_GENERIC, "%s %p out of range", name, ptr);
+
+	if (ADD_OVERFLOW((vaddr_t)ptr, sz, &max_addr))
+		err(TEE_ERROR_GENERIC, "%s range overflow", name);
+
+	if (max_addr > elf->max_addr)
+		err(TEE_ERROR_GENERIC,
+		    "%s %p..%#zx out of range", name, ptr, max_addr);
+}
+
 static void check_hashtab(struct ta_elf *elf, void *ptr, size_t num_buckets,
 			  size_t num_chains)
 {
@@ -203,23 +219,17 @@
 	 * See http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#hash
 	 */
 	size_t num_words = 2;
-	vaddr_t max_addr = 0;
 	size_t sz = 0;
 
-	if ((vaddr_t)ptr < elf->load_addr)
-		err(TEE_ERROR_GENERIC, "Hashtab %p out of range", ptr);
-
 	if (!ALIGNMENT_IS_OK(ptr, uint32_t))
 		err(TEE_ERROR_GENERIC, "Bad alignment of hashtab %p", ptr);
 
 	if (ADD_OVERFLOW(num_words, num_buckets, &num_words) ||
 	    ADD_OVERFLOW(num_words, num_chains, &num_words) ||
-	    MUL_OVERFLOW(num_words, sizeof(uint32_t), &sz) ||
-	    ADD_OVERFLOW((vaddr_t)ptr, sz, &max_addr))
+	    MUL_OVERFLOW(num_words, sizeof(uint32_t), &sz))
 		err(TEE_ERROR_GENERIC, "Hashtab overflow");
 
-	if (max_addr > elf->max_addr)
-		err(TEE_ERROR_GENERIC, "Hashtab %p out of range", ptr);
+	check_range(elf, "Hashtab", ptr, sz);
 }
 
 static void save_hashtab(struct ta_elf *elf)
@@ -254,10 +264,21 @@
 	size_t str_idx = shdr[tab_idx].sh_link;
 
 	elf->dynsymtab = (void *)(shdr[tab_idx].sh_addr + elf->load_addr);
-	assert(!(shdr[tab_idx].sh_size % sizeof(Elf32_Sym)));
+	if (!ALIGNMENT_IS_OK(elf->dynsymtab, Elf32_Sym))
+		err(TEE_ERROR_GENERIC, "Bad alignment of dynsymtab %p",
+		    elf->dynsymtab);
+	check_range(elf, "Dynsymtab", elf->dynsymtab, shdr[tab_idx].sh_size);
+
+	if (shdr[tab_idx].sh_size % sizeof(Elf32_Sym))
+		err(TEE_ERROR_GENERIC,
+		    "Size of dynsymtab not an even multiple of Elf32_Sym");
 	elf->num_dynsyms = shdr[tab_idx].sh_size / sizeof(Elf32_Sym);
 
+	if (str_idx >= elf->e_shnum)
+		err(TEE_ERROR_GENERIC, "Dynstr section index out of range");
 	elf->dynstr = (void *)(shdr[str_idx].sh_addr + elf->load_addr);
+	check_range(elf, "Dynstr", elf->dynstr, shdr[str_idx].sh_size);
+
 	elf->dynstr_size = shdr[str_idx].sh_size;
 }
 
@@ -268,10 +289,22 @@
 
 	elf->dynsymtab = (void *)(vaddr_t)(shdr[tab_idx].sh_addr +
 					   elf->load_addr);
-	assert(!(shdr[tab_idx].sh_size % sizeof(Elf64_Sym)));
+
+	if (!ALIGNMENT_IS_OK(elf->dynsymtab, Elf64_Sym))
+		err(TEE_ERROR_GENERIC, "Bad alignment of dynsymtab %p",
+		    elf->dynsymtab);
+	check_range(elf, "Dynsymtab", elf->dynsymtab, shdr[tab_idx].sh_size);
+
+	if (shdr[tab_idx].sh_size % sizeof(Elf64_Sym))
+		err(TEE_ERROR_GENERIC,
+		    "Size of dynsymtab not an even multiple of Elf64_Sym");
 	elf->num_dynsyms = shdr[tab_idx].sh_size / sizeof(Elf64_Sym);
 
+	if (str_idx >= elf->e_shnum)
+		err(TEE_ERROR_GENERIC, "Dynstr section index out of range");
 	elf->dynstr = (void *)(vaddr_t)(shdr[str_idx].sh_addr + elf->load_addr);
+	check_range(elf, "Dynstr", elf->dynstr, shdr[str_idx].sh_size);
+
 	elf->dynstr_size = shdr[str_idx].sh_size;
 }