Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 02 Apr 2026 02:42:15 +0000
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: ffbf3fecdeff - main - rtld: allow dlopen("#<number>/<path>")
Message-ID:  <69cdd787.360cc.29cd7c78@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=ffbf3fecdeffa17c0745e7ed342989acb620d68e

commit ffbf3fecdeffa17c0745e7ed342989acb620d68e
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-03-29 22:45:49 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-04-02 02:41:55 +0000

    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
---
 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 1cf0d3e9ba28..e3f5aa5be9b4 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 *);
@@ -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.


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69cdd787.360cc.29cd7c78>