rtld: allow dlopen("#<number>/<path>")
When a specially formatted path is passed to dlopen(), of the form #number/path and the number is the valid dirfd file descriptor listed in the LD_LIBRARY_FDS, interpret it as a relative path name against dirfd number. This complements the result returned from dladdr() for such objects in dli_fname. Reviewed by: markj Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D56152
This commit is contained in:
@@ -164,6 +164,20 @@ Symbols from the loaded library are put before global symbols when
|
||||
resolving symbolic references originated from the library.
|
||||
.El
|
||||
.Pp
|
||||
A special syntax for the
|
||||
.Fa path
|
||||
is supported, in the form of
|
||||
.Dl #number/name .
|
||||
The
|
||||
.Ql number
|
||||
should be a decimal number, which references an open file descriptor,
|
||||
and which must be also listed in the environment variable
|
||||
.Ev LD_LIBRARY_PATH_FDS .
|
||||
In this case, the linker tries to load an object that can be opened by
|
||||
.Ql openat(number, path, O_RDONLY) .
|
||||
This feature is only available to trusted processes, i.e.,
|
||||
the activated image must be not set-uid or set-gid.
|
||||
.Pp
|
||||
If
|
||||
.Fn dlopen
|
||||
fails, it returns a null pointer, and sets an error condition which may
|
||||
|
||||
+54
-3
@@ -178,6 +178,7 @@ static int symlook_obj1_sysv(SymLook *, const Obj_Entry *);
|
||||
static int symlook_obj1_gnu(SymLook *, const Obj_Entry *);
|
||||
static void *tls_get_addr_slow(struct tcb *, int, size_t, bool) __noinline;
|
||||
static void trace_loaded_objects(Obj_Entry *, bool);
|
||||
static int try_fds_open(const char *name, const char *path);
|
||||
static void unlink_object(Obj_Entry *);
|
||||
static void unload_object(Obj_Entry *, RtldLockState *lockstate);
|
||||
static void unref_dag(Obj_Entry *);
|
||||
@@ -2875,9 +2876,12 @@ load_object(const char *name, int fd_u, const Obj_Entry *refobj, int flags)
|
||||
* using stat().
|
||||
*/
|
||||
if ((fd = open(path, O_RDONLY | O_CLOEXEC | O_VERIFY)) == -1) {
|
||||
_rtld_error("Cannot open \"%s\"", path);
|
||||
free(path);
|
||||
return (NULL);
|
||||
fd = try_fds_open(path, ld_library_dirs);
|
||||
if (fd == -1) {
|
||||
_rtld_error("Cannot open \"%s\"", path);
|
||||
free(path);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fd = fcntl(fd_u, F_DUPFD_CLOEXEC, 0);
|
||||
@@ -3586,6 +3590,53 @@ rtld_nop_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse string of the format '#number/name", where number must be a
|
||||
* decimal number of the opened file descriptor listed in
|
||||
* LD_LIBRARY_PATH_FDS. If successful, tries to open dso name under
|
||||
* dirfd number and returns resulting fd.
|
||||
* On any error, returns -1.
|
||||
*/
|
||||
static int
|
||||
try_fds_open(const char *name, const char *path)
|
||||
{
|
||||
const char *n;
|
||||
char *envcopy, *fdstr, *last_token, *ncopy;
|
||||
size_t len;
|
||||
int fd, dirfd, dirfd_path;
|
||||
|
||||
if (!trust || name[0] != '#' || path == NULL)
|
||||
return (-1);
|
||||
|
||||
name++;
|
||||
n = strchr(name, '/');
|
||||
if (n == NULL)
|
||||
return (-1);
|
||||
len = n - name;
|
||||
ncopy = xmalloc(len + 1);
|
||||
memcpy(ncopy, name, len);
|
||||
ncopy[len] = '\0';
|
||||
dirfd = parse_integer(ncopy);
|
||||
free(ncopy);
|
||||
if (dirfd == -1)
|
||||
return (-1);
|
||||
|
||||
envcopy = xstrdup(path);
|
||||
dirfd_path = -1;
|
||||
for (fdstr = strtok_r(envcopy, ":", &last_token); fdstr != NULL;
|
||||
fdstr = strtok_r(NULL, ":", &last_token)) {
|
||||
dirfd_path = parse_integer(fdstr);
|
||||
if (dirfd_path == dirfd)
|
||||
break;
|
||||
}
|
||||
free(envcopy);
|
||||
if (dirfd_path != dirfd)
|
||||
return (-1);
|
||||
|
||||
fd = __sys_openat(dirfd, n + 1, O_RDONLY | O_CLOEXEC | O_VERIFY);
|
||||
return (fd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over a search path, translate each element, and invoke the
|
||||
* callback on the result.
|
||||
|
||||
Reference in New Issue
Block a user