about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2013-08-23 13:56:30 -0400
committerRich Felker <dalias@aerifal.cx>2013-08-23 13:56:30 -0400
commita897a20a57b461d388447858b9404fc81f107bcd (patch)
tree4dcece86a609731fc73d303d1c2cc1c6b9280113
parent709355e1f6dad17234606486400568bc87a37d79 (diff)
downloadmusl-a897a20a57b461d388447858b9404fc81f107bcd.tar.gz
musl-a897a20a57b461d388447858b9404fc81f107bcd.tar.xz
musl-a897a20a57b461d388447858b9404fc81f107bcd.zip
add rpath $ORIGIN processing to dynamic linker
-rw-r--r--src/ldso/dynlink.c62
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],