git: 8fb16a5c7f81 - stable/15 - rtld: allow dlopen("#<number>/<path>")
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 09 Apr 2026 08:25:47 UTC
The branch stable/15 has been updated by kib:
URL: https://cgit.FreeBSD.org/src/commit/?id=8fb16a5c7f81121a68b5aa185d70f7fdcb228da5
commit 8fb16a5c7f81121a68b5aa185d70f7fdcb228da5
Author: Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-03-29 22:45:49 +0000
Commit: Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-04-08 18:22:39 +0000
rtld: allow dlopen("#<number>/<path>")
(cherry picked from commit ffbf3fecdeffa17c0745e7ed342989acb620d68e)
---
lib/libc/gen/dlopen.3 | 14 ++++++++++++
libexec/rtld-elf/rtld.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 68 insertions(+), 3 deletions(-)
diff --git a/lib/libc/gen/dlopen.3 b/lib/libc/gen/dlopen.3
index 340545114114..2f10c17a4f53 100644
--- a/lib/libc/gen/dlopen.3
+++ b/lib/libc/gen/dlopen.3
@@ -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
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index 9e64d1bf8aee..db5d22dfefdb 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -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 *);
@@ -2877,9 +2878,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);
@@ -3588,6 +3592,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.