Permalink
Browse files

Implement the dynamic string token substitution in the rpath and

soneeded pathes. The $ORIGIN, $OSNAME, $OSREL and $PLATFORM tokens
are supported. Enabling the substitution requires DF_ORIGIN flag in
DT_FLAGS or DF_1_ORIGIN if DF_FLAGS_1, that may be set with -z origin
gnu ld flag. Translation is unconditionally disabled for setuid/setgid
processes.

The $ORIGIN translation relies on the AT_EXECPATH auxinfo supplied
by kernel.

Requested by:	maho
Tested by:	maho, pho
Reviewed by:	kan
  • Loading branch information...
kostikbel committed Mar 18, 2009
1 parent 79dc1e1 commit 46713086a1c3b7565c0b42b823b0d6d209228ce6
Showing with 148 additions and 10 deletions.
  1. +2 −0 libexec/rtld-elf/map_object.c
  2. +144 −9 libexec/rtld-elf/rtld.c
  3. +2 −1 libexec/rtld-elf/rtld.h
@@ -348,6 +348,8 @@ obj_free(Obj_Entry *obj)
free(obj->vertab);
if (obj->origin_path)
free(obj->origin_path);
if (obj->z_origin)
free(obj->rpath);
if (obj->priv)
free(obj->priv);
if (obj->path)
@@ -41,6 +41,7 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/utsname.h>
#include <sys/ktrace.h>
#include <dlfcn.h>
@@ -118,6 +119,7 @@ static void objlist_remove_unref(Objlist *);
static void *path_enumerate(const char *, path_enum_proc, void *);
static int relocate_objects(Obj_Entry *, bool, Obj_Entry *);
static int rtld_dirname(const char *, char *);
static int rtld_dirname_abs(const char *, char *);
static void rtld_exit(void);
static char *search_library_path(const char *, const char *);
static const void **get_program_var_addr(const char *);
@@ -134,6 +136,9 @@ static void unlink_object(Obj_Entry *);
static void unload_object(Obj_Entry *);
static void unref_dag(Obj_Entry *);
static void ref_dag(Obj_Entry *);
static int origin_subst_one(char **res, const char *real, const char *kw,
const char *subst, char *may_free);
static char *origin_subst(const char *real, const char *origin_path);
static int rtld_verify_versions(const Objlist *);
static int rtld_verify_object_versions(Obj_Entry *);
static void object_add_name(Obj_Entry *, const char *);
@@ -412,7 +417,25 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
die();
}
obj_main->path = xstrdup(argv0);
if (aux_info[AT_EXECPATH] != 0) {
char *kexecpath;
char buf[MAXPATHLEN];
kexecpath = aux_info[AT_EXECPATH]->a_un.a_ptr;
dbg("AT_EXECPATH %p %s", kexecpath, kexecpath);
if (kexecpath[0] == '/')
obj_main->path = kexecpath;
else if (getcwd(buf, sizeof(buf)) == NULL ||
strlcat(buf, "/", sizeof(buf)) >= sizeof(buf) ||
strlcat(buf, kexecpath, sizeof(buf)) >= sizeof(buf))
obj_main->path = xstrdup(argv0);
else
obj_main->path = xstrdup(buf);
} else {
dbg("No AT_EXECPATH");
obj_main->path = xstrdup(argv0);
}
dbg("obj_main path %s", obj_main->path);
obj_main->mainprog = true;
/*
@@ -614,6 +637,83 @@ basename(const char *name)
return p != NULL ? p + 1 : name;
}
static struct utsname uts;
static int
origin_subst_one(char **res, const char *real, const char *kw, const char *subst,
char *may_free)
{
const char *p, *p1;
char *res1;
int subst_len;
int kw_len;
res1 = *res = NULL;
p = real;
subst_len = kw_len = 0;
for (;;) {
p1 = strstr(p, kw);
if (p1 != NULL) {
if (subst_len == 0) {
subst_len = strlen(subst);
kw_len = strlen(kw);
}
if (*res == NULL) {
*res = xmalloc(PATH_MAX);
res1 = *res;
}
if ((res1 - *res) + subst_len + (p1 - p) >= PATH_MAX) {
_rtld_error("Substitution of %s in %s cannot be performed",
kw, real);
if (may_free != NULL)
free(may_free);
free(res);
return (false);
}
memcpy(res1, p, p1 - p);
res1 += p1 - p;
memcpy(res1, subst, subst_len);
res1 += subst_len;
p = p1 + kw_len;
} else {
if (*res == NULL) {
if (may_free != NULL)
*res = may_free;
else
*res = xstrdup(real);
return (true);
}
*res1 = '\0';
if (may_free != NULL)
free(may_free);
if (strlcat(res1, p, PATH_MAX - (res1 - *res)) >= PATH_MAX) {
free(res);
return (false);
}
return (true);
}
}
}
static char *
origin_subst(const char *real, const char *origin_path)
{
char *res1, *res2, *res3, *res4;
if (uts.sysname[0] == '\0') {
if (uname(&uts) != 0) {
_rtld_error("utsname failed: %d", errno);
return (NULL);
}
}
if (!origin_subst_one(&res1, real, "$ORIGIN", origin_path, NULL) ||
!origin_subst_one(&res2, res1, "$OSNAME", uts.sysname, res1) ||
!origin_subst_one(&res3, res2, "$OSREL", uts.release, res2) ||
!origin_subst_one(&res4, res3, "$PLATFORM", uts.machine, res3))
return (NULL);
return (res4);
}
static void
die(void)
{
@@ -790,11 +890,8 @@ digest_dynamic(Obj_Entry *obj, int early)
#endif
case DT_FLAGS:
if (dynp->d_un.d_val & DF_ORIGIN) {
obj->origin_path = xmalloc(PATH_MAX);
if (rtld_dirname(obj->path, obj->origin_path) == -1)
die();
}
if ((dynp->d_un.d_val & DF_1_ORIGIN) && trust)
obj->z_origin = true;
if (dynp->d_un.d_val & DF_SYMBOLIC)
obj->symbolic = true;
if (dynp->d_un.d_val & DF_TEXTREL)
@@ -826,6 +923,15 @@ digest_dynamic(Obj_Entry *obj, int early)
break;
#endif
case DT_FLAGS_1:
if ((dynp->d_un.d_val & DF_1_ORIGIN) && trust)
obj->z_origin = true;
if (dynp->d_un.d_val & DF_1_GLOBAL)
/* XXX */;
if (dynp->d_un.d_val & DF_1_BIND_NOW)
obj->bind_now = true;
break;
default:
if (!early) {
dbg("Ignoring d_tag %ld = %#lx", (long)dynp->d_tag,
@@ -844,8 +950,17 @@ digest_dynamic(Obj_Entry *obj, int early)
obj->pltrelsize = 0;
}
if (dyn_rpath != NULL)
obj->rpath = obj->strtab + dyn_rpath->d_un.d_val;
if (obj->z_origin && obj->origin_path == NULL) {
obj->origin_path = xmalloc(PATH_MAX);
if (rtld_dirname_abs(obj->path, obj->origin_path) == -1)
die();
}
if (dyn_rpath != NULL) {
obj->rpath = (char *)obj->strtab + dyn_rpath->d_un.d_val;
if (obj->z_origin)
obj->rpath = origin_subst(obj->rpath, obj->origin_path);
}
if (dyn_soname != NULL)
object_add_name(obj, obj->strtab + dyn_soname->d_un.d_val);
@@ -1003,7 +1118,10 @@ find_library(const char *xname, const Obj_Entry *refobj)
xname);
return NULL;
}
return xstrdup(xname);
if (refobj->z_origin)
return origin_subst(xname, refobj->origin_path);
else
return xstrdup(xname);
}
if (libmap_disable || (refobj == NULL) ||
@@ -2309,6 +2427,23 @@ rtld_dirname(const char *path, char *bname)
return (0);
}
static int
rtld_dirname_abs(const char *path, char *base)
{
char base_rel[PATH_MAX];
if (rtld_dirname(path, base) == -1)
return (-1);
if (base[0] == '/')
return (0);
if (getcwd(base_rel, sizeof(base_rel)) == NULL ||
strlcat(base_rel, "/", sizeof(base_rel)) >= sizeof(base_rel) ||
strlcat(base_rel, base, sizeof(base_rel)) >= sizeof(base_rel))
return (-1);
strcpy(base, base_rel);
return (0);
}
static void
linkmap_add(Obj_Entry *obj)
{
@@ -195,7 +195,7 @@ typedef struct Struct_Obj_Entry {
const Elf_Hashelt *chains; /* Hash table chain array */
unsigned long nchains; /* Number of chains */
const char *rpath; /* Search path specified in object */
char *rpath; /* Search path specified in object */
Needed_Entry *needed; /* Shared objects needed by this one (%) */
STAILQ_HEAD(, Struct_Name_Entry) names; /* List of names for this object we
@@ -216,6 +216,7 @@ typedef struct Struct_Obj_Entry {
bool init_done : 1; /* Already have added object to init list */
bool tls_done : 1; /* Already allocated offset for static TLS */
bool phdr_alloc : 1; /* Phdr is allocated and needs to be freed. */
bool z_origin : 1; /* Process rpath and soname tokens */
struct link_map linkmap; /* for GDB and dlinfo() */
Objlist dldags; /* Object belongs to these dlopened DAGs (%) */

0 comments on commit 4671308

Please sign in to comment.