diff options
| author | Rich Felker <dalias@aerifal.cx> | 2013-07-10 14:38:20 -0400 | 
|---|---|---|
| committer | Rich Felker <dalias@aerifal.cx> | 2013-07-10 14:38:20 -0400 | 
| commit | 30763fd01bef85f30e79baa30173674c007690cc (patch) | |
| tree | 35f16a19bb39fe7d3f4657f60c90365a57cda584 | |
| parent | c713d8797804903b54203a645e023e2077c7556d (diff) | |
| download | musl-30763fd01bef85f30e79baa30173674c007690cc.tar.gz | |
fix invalid library phdr pointers passed to callback from dl_iterate_phdr
map_library was saving pointers to an automatic-storage buffer rather
than pointers into the mapping. this should be a fairly simple fix,
but the patch here is slightly complicated by two issues:
1. supporting gratuitously obfuscated ELF files where the program
headers are not right at the beginning of the file.
2. cleaning up the map_library function so that data isn't clobbered
by the time we need it.
| -rw-r--r-- | src/ldso/dynlink.c | 25 | 
1 files changed, 16 insertions, 9 deletions
diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c index 7031d03a..ff5b738d 100644 --- a/src/ldso/dynlink.c +++ b/src/ldso/dynlink.c @@ -309,7 +309,7 @@ static void *map_library(int fd, struct dso *dso)  	size_t this_min, this_max;  	off_t off_start;  	Ehdr *eh; -	Phdr *ph; +	Phdr *ph, *ph0;  	unsigned prot;  	unsigned char *map, *base;  	size_t dyn; @@ -324,11 +324,10 @@ static void *map_library(int fd, struct dso *dso)  	if (eh->e_phoff + phsize > l) {  		l = pread(fd, buf+1, phsize, eh->e_phoff);  		if (l != phsize) return 0; -		eh->e_phoff = sizeof *eh; +		ph = ph0 = (void *)(buf + 1); +	} else { +		ph = ph0 = (void *)((char *)buf + eh->e_phoff);  	} -	ph = (void *)((char *)buf + eh->e_phoff); -	dso->phdr = ph; -	dso->phnum = eh->e_phnum;  	for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {  		if (ph->p_type == PT_DYNAMIC)  			dyn = ph->p_vaddr; @@ -363,9 +362,18 @@ static void *map_library(int fd, struct dso *dso)  	map = mmap((void *)addr_min, map_len, prot, MAP_PRIVATE, fd, off_start);  	if (map==MAP_FAILED) return 0;  	base = map - addr_min; -	ph = (void *)((char *)buf + eh->e_phoff); -	for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) { +	dso->phdr = 0; +	dso->phnum = 0; +	for (ph=ph0, i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {  		if (ph->p_type != PT_LOAD) continue; +		/* Check if the programs headers are in this load segment, and +		 * if so, record the address for use by dl_iterate_phdr. */ +		if (!dso->phdr && eh->e_phoff >= ph->p_offset +		    && eh->e_phoff+phsize <= ph->p_offset+ph->p_filesz) { +			dso->phdr = (void *)(base + ph->p_vaddr +				+ (eh->e_phoff-ph->p_offset)); +			dso->phnum = eh->e_phnum; +		}  		/* Reuse the existing mapping for the lowest-address LOAD */  		if ((ph->p_vaddr & -PAGE_SIZE) == addr_min) continue;  		this_min = ph->p_vaddr & -PAGE_SIZE; @@ -390,8 +398,7 @@ static void *map_library(int fd, struct dso *dso)  				goto error;  			break;  		} -	if (!runtime) reclaim_gaps(base, (void *)((char *)buf + eh->e_phoff), -		eh->e_phentsize, eh->e_phnum); +	if (!runtime) reclaim_gaps(base, ph0, eh->e_phentsize, eh->e_phnum);  	dso->map = map;  	dso->map_len = map_len;  	dso->base = base;  | 
