diff options
Diffstat (limited to 'src/ldso/dynlink.c')
-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], |