diff options
| -rw-r--r-- | src/ldso/dynlink.c | 62 | 
1 files changed, 59 insertions, 3 deletions
| diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c index 8c5b9625..6f23fa54 100644 --- a/src/ldso/dynlink.c +++ b/src/ldso/dynlink.c @@ -72,8 +72,9 @@ struct dso {  	signed char global;  	char relocated;  	char constructed; +	char kernel_mapped;  	struct dso **deps, *needed_by; -	char *rpath; +	char *rpath_orig, *rpath;  	void *tls_image;  	size_t tls_len, tls_size, tls_align, tls_id, tls_offset;  	void **new_dtv; @@ -448,6 +449,58 @@ static int path_open(const char *name, const char *s, char *buf, size_t buf_size  	}  } +static int fixup_rpath(struct dso *p, char *buf, size_t buf_size) +{ +	size_t n, l; +	const char *s, *t, *origin; +	char *d; +	if (p->rpath) return 0; +	if (!p->rpath_orig) return -1; +	if (!strchr(p->rpath_orig, '$')) { +		p->rpath = p->rpath_orig; +		return 0; +	} +	n = 0; +	s = p->rpath_orig; +	while ((t=strstr(s, "$ORIGIN")) || (t=strstr(s, "${ORIGIN}"))) { +		s = t+1; +		n++; +	} +	if (n > SSIZE_MAX/PATH_MAX) return -1; + +	if (p->kernel_mapped) { +		/* $ORIGIN searches cannot be performed for the main program +		 * when it is suid/sgid/AT_SECURE. This is because the +		 * pathname is under the control of the caller of execve. +		 * For libraries, however, $ORIGIN can be processed safely +		 * since the library's pathname came from a trusted source +		 * (either system paths or a call to dlopen). */ +		if (libc.secure) +			return -1; +		if (readlink("/proc/self/exe", buf, buf_size) >= buf_size) +			return -1; +		origin = buf; +	} else { +		origin = p->name; +	} +	t = strrchr(origin, '/'); +	l = t ? t-origin : 0; +	p->rpath = malloc(strlen(p->rpath_orig) + n*l + 1); +	if (!p->rpath) return -1; + +	d = p->rpath; +	s = p->rpath_orig; +	while ((t=strstr(s, "$ORIGIN")) || (t=strstr(s, "${ORIGIN}"))) { +		memcpy(d, s, t-s); +		d += t-s; +		memcpy(d, origin, l); +		d += l; +		s = t + 7 + 2*(t[1]=='{'); +	} +	strcpy(d, s); +	return 0; +} +  static void decode_dyn(struct dso *p)  {  	size_t dyn[DYN_CNT] = {0}; @@ -457,7 +510,7 @@ static void decode_dyn(struct dso *p)  	if (dyn[0]&(1<<DT_HASH))  		p->hashtab = (void *)(p->base + dyn[DT_HASH]);  	if (dyn[0]&(1<<DT_RPATH)) -		p->rpath = (void *)(p->strings + dyn[DT_RPATH]); +		p->rpath_orig = (void *)(p->strings + dyn[DT_RPATH]);  	if (search_vec(p->dynv, dyn, DT_GNU_HASH))  		p->ghashtab = (void *)(p->base + *dyn);  	if (search_vec(p->dynv, dyn, DT_VERSYM)) @@ -525,7 +578,7 @@ static struct dso *load_library(const char *name, struct dso *needed_by)  		fd = -1;  		if (env_path) fd = path_open(name, env_path, buf, sizeof buf);  		for (p=needed_by; fd < 0 && p; p=p->needed_by) -			if (p->rpath) +			if (!fixup_rpath(p, buf, sizeof buf))  				fd = path_open(name, p->rpath, buf, sizeof buf);  		if (fd < 0) {  			if (!sys_path) { @@ -917,6 +970,7 @@ void *__dynlink(int argc, char **argv)  	  || aux[AT_GID]!=aux[AT_EGID] || aux[AT_SECURE]) {  		env_path = 0;  		env_preload = 0; +		libc.secure = 1;  	}  	/* If the dynamic linker was invoked as a program itself, AT_BASE @@ -933,6 +987,7 @@ void *__dynlink(int argc, char **argv)  	lib->base = (void *)aux[AT_BASE];  	lib->name = lib->shortname = "libc.so";  	lib->global = 1; +	lib->kernel_mapped = 1;  	ehdr = (void *)lib->base;  	lib->phnum = ehdr->e_phnum;  	lib->phdr = (void *)(aux[AT_BASE]+ehdr->e_phoff); @@ -962,6 +1017,7 @@ void *__dynlink(int argc, char **argv)  		if (app->tls_size) app->tls_image = (char *)app->base + tls_image;  		if (interp_off) lib->name = (char *)app->base + interp_off;  		app->name = argv[0]; +		app->kernel_mapped = 1;  		app->dynv = (void *)(app->base + find_dyn(  			(void *)aux[AT_PHDR], aux[AT_PHNUM], aux[AT_PHENT]));  		find_map_range((void *)aux[AT_PHDR], | 
