about summary refs log tree commit diff
path: root/src/ldso
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2013-08-02 09:56:49 -0400
committerRich Felker <dalias@aerifal.cx>2013-08-02 09:56:49 -0400
commitd5884a574cf156c8346e84b495b5cb29bbbd2d1a (patch)
tree64eb0ebd4abbe2149722d3c4915a82d0dd8a439c /src/ldso
parent7443dd271c09e8209a9478388fcd64a633b485e7 (diff)
downloadmusl-d5884a574cf156c8346e84b495b5cb29bbbd2d1a.tar.gz
musl-d5884a574cf156c8346e84b495b5cb29bbbd2d1a.tar.xz
musl-d5884a574cf156c8346e84b495b5cb29bbbd2d1a.zip
improve error handling in map_library and support long phdrs
previously, errno could be meaningless when the caller wrote it to the
dlerror string or stderr. try to make it meaningful. also, fix
incorrect check for over-long program headers and instead actually
support them by allocating memory if needed.
Diffstat (limited to 'src/ldso')
-rw-r--r--src/ldso/dynlink.c33
1 files changed, 21 insertions, 12 deletions
diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c
index 63a25548..0ae7177f 100644
--- a/src/ldso/dynlink.c
+++ b/src/ldso/dynlink.c
@@ -305,6 +305,7 @@ static void reclaim_gaps(unsigned char *base, Phdr *ph, size_t phent, size_t phc
 static void *map_library(int fd, struct dso *dso)
 {
 	Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)];
+	void *allocated_buf=0;
 	size_t phsize;
 	size_t addr_min=SIZE_MAX, addr_max=0, map_len;
 	size_t this_min, this_max;
@@ -312,23 +313,28 @@ static void *map_library(int fd, struct dso *dso)
 	Ehdr *eh;
 	Phdr *ph, *ph0;
 	unsigned prot;
-	unsigned char *map, *base;
+	unsigned char *map=MAP_FAILED, *base;
 	size_t dyn=0;
 	size_t tls_image=0;
 	size_t i;
 
 	ssize_t l = read(fd, buf, sizeof buf);
-	if (l<(int)sizeof *eh) return 0;
 	eh = buf;
-	if (eh->e_type != ET_DYN && eh->e_type != ET_EXEC) {
-		errno = ENOEXEC;
-		return 0;
-	}
+	if (l<0) return 0;
+	if (l<sizeof *eh || (eh->e_type != ET_DYN && eh->e_type != ET_EXEC))
+		goto noexec;
 	phsize = eh->e_phentsize * eh->e_phnum;
-	if (phsize + sizeof *eh > l) return 0;
-	if (eh->e_phoff + phsize > l) {
+	if (phsize > sizeof buf - sizeof *eh) {
+		allocated_buf = malloc(phsize);
+		if (!allocated_buf) return 0;
+		l = pread(fd, allocated_buf, phsize, eh->e_phoff);
+		if (l < 0) goto error;
+		if (l != phsize) goto noexec;
+		ph = ph0 = allocated_buf;
+	} else if (eh->e_phoff + phsize > l) {
 		l = pread(fd, buf+1, phsize, eh->e_phoff);
-		if (l != phsize) return 0;
+		if (l < 0) goto error;
+		if (l != phsize) goto noexec;
 		ph = ph0 = (void *)(buf + 1);
 	} else {
 		ph = ph0 = (void *)((char *)buf + eh->e_phoff);
@@ -354,7 +360,7 @@ static void *map_library(int fd, struct dso *dso)
 			addr_max = ph->p_vaddr+ph->p_memsz;
 		}
 	}
-	if (!dyn) return 0;
+	if (!dyn) goto noexec;
 	addr_max += PAGE_SIZE-1;
 	addr_max &= -PAGE_SIZE;
 	addr_min &= -PAGE_SIZE;
@@ -365,7 +371,7 @@ static void *map_library(int fd, struct dso *dso)
 	 * use the invalid part; we just need to reserve the right
 	 * amount of virtual address space to map over later. */
 	map = mmap((void *)addr_min, map_len, prot, MAP_PRIVATE, fd, off_start);
-	if (map==MAP_FAILED) return 0;
+	if (map==MAP_FAILED) goto error;
 	/* If the loaded file is not relocatable and the requested address is
 	 * not available, then the load operation must fail. */
 	if (eh->e_type != ET_DYN && addr_min && map!=(void *)addr_min) {
@@ -416,8 +422,11 @@ static void *map_library(int fd, struct dso *dso)
 	dso->dynv = (void *)(base+dyn);
 	if (dso->tls_size) dso->tls_image = (void *)(base+tls_image);
 	return map;
+noexec:
+	errno = ENOEXEC;
 error:
-	munmap(map, map_len);
+	if (map!=MAP_FAILED) munmap(map, map_len);
+	free(allocated_buf);
 	return 0;
 }