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:
Konstantin Belousov
2026-03-30 01:45:49 +03:00
parent 51db69c14f
commit ffbf3fecde
2 changed files with 68 additions and 3 deletions
+14
View File
@@ -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
View File
@@ -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.