Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 24 Mar 2021 22:40:27 GMT
From:      Mariusz Zaborski <oshogbo@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: f90218886fc8 - main - rtld: introduce PRELOAD_FDS
Message-ID:  <202103242240.12OMeRHq089980@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by oshogbo:

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

commit f90218886fc82e7b1fdb9e241adc5d713dadabe3
Author:     Mariusz Zaborski <oshogbo@FreeBSD.org>
AuthorDate: 2021-03-24 21:10:33 +0000
Commit:     Mariusz Zaborski <oshogbo@FreeBSD.org>
CommitDate: 2021-03-24 22:40:48 +0000

    rtld: introduce PRELOAD_FDS
    
    The new PRELOAD_FDS variable accepts a list of file descriptors
    that should be loaded into the process.
    
    This may be used to optimize a loading process - in the case when
    we already have a file descriptor to the library; we don't have
    to look into multiple PATH to find it.
    
    It may also be used in capability mode to load a single additional
    library without the need to open a directory that contains it.
    
    The last use of this functionality t may be a race-free method
    of loading libraries.
    
    Reviewed by:    kib, markj
    Differential Revision:  https://reviews.freebsd.org/D29334
---
 libexec/rtld-elf/rtld.1                     |  10 ++-
 libexec/rtld-elf/rtld.c                     |  33 +++++++--
 libexec/rtld-elf/tests/Makefile             |   7 ++
 libexec/rtld-elf/tests/common.c             |  81 +++++++++++++++++++++
 libexec/rtld-elf/tests/common.h             |  43 +++++++++++
 libexec/rtld-elf/tests/ld_library_pathfds.c |  56 +--------------
 libexec/rtld-elf/tests/ld_preload_fds.c     | 108 ++++++++++++++++++++++++++++
 7 files changed, 277 insertions(+), 61 deletions(-)

diff --git a/libexec/rtld-elf/rtld.1 b/libexec/rtld-elf/rtld.1
index 10dd7986903a..040ab496ceb3 100644
--- a/libexec/rtld-elf/rtld.1
+++ b/libexec/rtld-elf/rtld.1
@@ -28,7 +28,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd June 1, 2020
+.Dd March 24, 2021
 .Dt RTLD 1
 .Os
 .Sh NAME
@@ -189,6 +189,14 @@ to be linked in before any
 other shared libraries.
 If the directory is not specified then
 the directories specified by
+.It Ev LD_PRELOAD_PATH_FDS
+A colon separated list of file descriptor numbers for libraries.
+This is intended for preloading libraries in which we already have a file
+descriptor.
+This may optimize the process of loading libraries because we do not have to
+look for them in directories.
+It may also be useful in a capability base system where we do not have access to
+global namespaces such as the filesystem.
 .Ev LD_LIBRARY_PATH
 will be searched first
 followed by the set of built-in standard directories.
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index 3ecf50b8cf5d..733c3c80b70f 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -119,7 +119,7 @@ static void linkmap_delete(Obj_Entry *);
 static void load_filtees(Obj_Entry *, int flags, RtldLockState *);
 static void unload_filtees(Obj_Entry *, RtldLockState *);
 static int load_needed_objects(Obj_Entry *, int);
-static int load_preload_objects(void);
+static int load_preload_objects(char *, bool);
 static Obj_Entry *load_object(const char *, int fd, const Obj_Entry *, int);
 static void map_stacks_exec(RtldLockState *);
 static int obj_disable_relro(Obj_Entry *);
@@ -210,6 +210,8 @@ static char *ld_library_path;	/* Environment variable for search path */
 static char *ld_library_dirs;	/* Environment variable for library descriptors */
 static char *ld_preload;	/* Environment variable for libraries to
 				   load first */
+static char *ld_preload_fds;	/* Environment variable for libraries represented by
+				   descriptors */
 static const char *ld_elf_hints_path;	/* Environment variable for alternative hints path */
 static const char *ld_tracing;	/* Called from ldd to print libs */
 static char *ld_utrace;		/* Use utrace() to log events. */
@@ -564,7 +566,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
 
     ld_bind_now = getenv(_LD("BIND_NOW"));
 
-    /* 
+    /*
      * If the process is tainted, then we un-set the dangerous environment
      * variables.  The process will be marked as tainted until setuid(2)
      * is called.  If any child process calls setuid(2) we do not want any
@@ -575,7 +577,8 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
 	    unsetenv(_LD("LIBRARY_PATH")) || unsetenv(_LD("LIBRARY_PATH_FDS")) ||
 	    unsetenv(_LD("LIBMAP_DISABLE")) || unsetenv(_LD("BIND_NOT")) ||
 	    unsetenv(_LD("DEBUG")) || unsetenv(_LD("ELF_HINTS_PATH")) ||
-	    unsetenv(_LD("LOADFLTR")) || unsetenv(_LD("LIBRARY_PATH_RPATH"))) {
+	    unsetenv(_LD("LOADFLTR")) || unsetenv(_LD("LIBRARY_PATH_RPATH")) ||
+	    unsetenv(_LD("PRELOAD_FDS"))) {
 		_rtld_error("environment corrupt; aborting");
 		rtld_die();
 	}
@@ -588,6 +591,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
     ld_library_path = getenv(_LD("LIBRARY_PATH"));
     ld_library_dirs = getenv(_LD("LIBRARY_PATH_FDS"));
     ld_preload = getenv(_LD("PRELOAD"));
+    ld_preload_fds = getenv(_LD("PRELOAD_FDS"));
     ld_elf_hints_path = getenv(_LD("ELF_HINTS_PATH"));
     ld_loadfltr = getenv(_LD("LOADFLTR")) != NULL;
     library_path_rpath = getenv(_LD("LIBRARY_PATH_RPATH"));
@@ -702,8 +706,12 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
     if (!libmap_disable)
         libmap_disable = (bool)lm_init(libmap_override);
 
+    dbg("loading LD_PRELOAD_FDS libraries");
+    if (load_preload_objects(ld_preload_fds, true) == -1)
+	rtld_die();
+
     dbg("loading LD_PRELOAD libraries");
-    if (load_preload_objects() == -1)
+    if (load_preload_objects(ld_preload, false) == -1)
 	rtld_die();
     preload_tail = globallist_curr(TAILQ_LAST(&obj_list, obj_entry_q));
 
@@ -2468,9 +2476,8 @@ load_needed_objects(Obj_Entry *first, int flags)
 }
 
 static int
-load_preload_objects(void)
+load_preload_objects(char *p, bool isfd)
 {
-	char *p = ld_preload;
 	Obj_Entry *obj;
 	static const char delim[] = " \t:;";
 
@@ -2479,12 +2486,24 @@ load_preload_objects(void)
 
 	p += strspn(p, delim);
 	while (*p != '\0') {
+		const char *name;
 		size_t len = strcspn(p, delim);
 		char savech;
+		int fd;
 
 		savech = p[len];
 		p[len] = '\0';
-		obj = load_object(p, -1, NULL, 0);
+		if (isfd) {
+			name = NULL;
+			fd = parse_integer(p);
+			if (fd == -1)
+				return (-1);
+		} else {
+			name = p;
+			fd = -1;
+		}
+
+		obj = load_object(name, fd, NULL, 0);
 		if (obj == NULL)
 			return (-1);	/* XXX - cleanup */
 		obj->z_interpose = true;
diff --git a/libexec/rtld-elf/tests/Makefile b/libexec/rtld-elf/tests/Makefile
index 4ab69c1ab0ab..6b8b4e80333b 100644
--- a/libexec/rtld-elf/tests/Makefile
+++ b/libexec/rtld-elf/tests/Makefile
@@ -3,7 +3,14 @@
 SUBDIR+=	libpythagoras target
 
 SUBDIR_DEPEND_target=	libpythagoras
+
 ATF_TESTS_C=	ld_library_pathfds
+ATF_TESTS_C+=	ld_preload_fds
+
+.for t in ${ATF_TESTS_C}
+SRCS.$t=	$t.c common.c
+.endfor
+
 WARNS?=		3
 
 .include <bsd.test.mk>
diff --git a/libexec/rtld-elf/tests/common.c b/libexec/rtld-elf/tests/common.c
new file mode 100644
index 000000000000..6eb3edfbc14c
--- /dev/null
+++ b/libexec/rtld-elf/tests/common.c
@@ -0,0 +1,81 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ * Copyright 2014 Jonathan Anderson.
+ * Copyright 2021 Mariusz Zaborski <oshogbo@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "common.h"
+
+void
+expect_success(int binary, char *senv)
+{
+	char * const env[] = { senv, NULL };
+
+	try_to_run(binary, 0, env, "the hypotenuse of 3 and 4 is 5\n", "");
+}
+
+void
+expect_missing_library(int binary, char *senv)
+{
+	char * const env[] = { senv, NULL };
+
+	try_to_run(binary, 1, env, "",
+	   "ld-elf.so.1: Shared object \"libpythagoras.so.0\" not found,"
+	    " required by \"target\"\n");
+}
+
+void
+try_to_run(int binary, int exit_status, char * const *env,
+        const char *expected_out, const char *expected_err)
+{
+	pid_t child = atf_utils_fork();
+
+	if (child == 0) {
+		char * const args[] = { "target", NULL };
+
+		fexecve(binary, args, env);
+		atf_tc_fail("fexecve() failed");
+	}
+
+	atf_utils_wait(child, exit_status, expected_out, expected_err);
+}
+
+int
+opendir(const char *name)
+{
+
+	return open(name, O_RDONLY | O_DIRECTORY);
+}
+
+int
+opendirat(int parent, const char *name)
+{
+
+	return openat(parent, name, O_RDONLY | O_DIRECTORY);
+}
diff --git a/libexec/rtld-elf/tests/common.h b/libexec/rtld-elf/tests/common.h
new file mode 100644
index 000000000000..d5bf2050dcfd
--- /dev/null
+++ b/libexec/rtld-elf/tests/common.h
@@ -0,0 +1,43 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ * Copyright 2014 Jonathan Anderson.
+ * Copyright 2021 Mariusz Zaborski <oshogbo@vexillium.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LD_COMMON_H_
+#define _LD_COMMON_H_
+
+#define	TARGET_ELF_NAME	"target"
+#define	TARGET_LIBRARY	"libpythagoras.so.0"
+
+void	expect_success(int binary, char *senv);
+void	expect_missing_library(int binary, char *senv);
+
+void	try_to_run(int binary, int expected_exit_status, char * const *env,
+	    const char *expected_out, const char *expected_err);
+int	opendir(const char *name);
+int	opendirat(int parent, const char *name);
+
+#endif /* _LD_COMMON_H_ */
diff --git a/libexec/rtld-elf/tests/ld_library_pathfds.c b/libexec/rtld-elf/tests/ld_library_pathfds.c
index 5c6c2dabbcdb..bc0627d4c3d5 100644
--- a/libexec/rtld-elf/tests/ld_library_pathfds.c
+++ b/libexec/rtld-elf/tests/ld_library_pathfds.c
@@ -29,6 +29,7 @@
 #include <fcntl.h>
 #include <stdio.h>
 
+#include "common.h"
 
 struct descriptors {
 	int	binary;
@@ -38,14 +39,8 @@ struct descriptors {
 	int	usr;
 };
 
-static void	setup(struct descriptors *, const atf_tc_t *);
-static void	expect_success(int binary, char *pathfds);
-static void	expect_missing_library(int binary, char *pathfds);
 
-static void	try_to_run(int binary, int expected_exit_status,
-    char * const *env, const char *expected_out, const char *expected_err);
-static int	opendir(const char *name);
-static int	opendirat(int parent, const char *name);
+static void	setup(struct descriptors *, const atf_tc_t *);
 
 
 ATF_TC_WITHOUT_HEAD(missing_library);
@@ -167,55 +162,10 @@ setup(struct descriptors *dp, const atf_tc_t *tc)
 	dp->testdir = opendir(atf_tc_get_config_var(tc, "srcdir"));
 	ATF_REQUIRE(dp->testdir >= 0);
 	ATF_REQUIRE(
-	    (dp->binary = openat(dp->testdir, "target", O_RDONLY)) >= 0);
+	    (dp->binary = openat(dp->testdir, TARGET_ELF_NAME, O_RDONLY)) >= 0);
 
 	ATF_REQUIRE((dp->root = opendir("/")) >= 0);
 	ATF_REQUIRE((dp->etc = opendirat(dp->root, "etc")) >= 0);
 	ATF_REQUIRE((dp->usr = opendirat(dp->root, "usr")) >= 0);
 }
 
-static void
-expect_success(int binary, char *pathfds)
-{
-	char * const env[] = { pathfds, NULL };
-	try_to_run(binary, 0, env, "the hypotenuse of 3 and 4 is 5\n", "");
-}
-
-static void
-expect_missing_library(int binary, char *pathfds)
-{
-	char * const env[] = { pathfds, NULL };
-	try_to_run(binary, 1, env, "",
-	   "ld-elf.so.1: Shared object \"libpythagoras.so.0\" not found,"
-	    " required by \"target\"\n");
-}
-
-
-static void
-try_to_run(int binary, int exit_status, char * const *env,
-        const char *expected_out, const char *expected_err)
-{
-	pid_t child = atf_utils_fork();
-
-	if (child == 0) {
-		char * const args[] = { "target", NULL };
-
-		fexecve(binary, args, env);
-		atf_tc_fail("fexecve() failed");
-	}
-
-	atf_utils_wait(child, exit_status, expected_out, expected_err);
-}
-
-
-static int
-opendir(const char *name)
-{
-	return open(name, O_RDONLY | O_DIRECTORY);
-}
-
-static int
-opendirat(int parent, const char *name)
-{
-	return openat(parent, name, O_RDONLY | O_DIRECTORY);
-}
diff --git a/libexec/rtld-elf/tests/ld_preload_fds.c b/libexec/rtld-elf/tests/ld_preload_fds.c
new file mode 100644
index 000000000000..3a220b009bb6
--- /dev/null
+++ b/libexec/rtld-elf/tests/ld_preload_fds.c
@@ -0,0 +1,108 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ * Copyright 2021 Mariusz Zaborski <oshogbo@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "common.h"
+
+int binaryfd;
+int libraryfd;
+
+static void
+setup(const atf_tc_t *tc)
+{
+	int testdir;
+
+	testdir = opendir(atf_tc_get_config_var(tc, "srcdir"));
+	ATF_REQUIRE(testdir >= 0);
+
+	binaryfd = openat(testdir, TARGET_ELF_NAME, O_RDONLY);
+	ATF_REQUIRE(binaryfd >= 0);
+	libraryfd = openat(testdir, TARGET_LIBRARY, O_RDONLY);
+	ATF_REQUIRE(libraryfd >= 0);
+
+	close(testdir);
+}
+
+ATF_TC_WITHOUT_HEAD(missing_library);
+ATF_TC_BODY(missing_library, tc)
+{
+
+	setup(tc);
+	expect_missing_library(binaryfd, NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(bad_librarys);
+ATF_TC_BODY(bad_librarys, tc)
+{
+	char *senv;
+
+	ATF_REQUIRE(asprintf(&senv, "LD_PRELOAD_FDS=::") > 0);
+
+	setup(tc);
+	expect_missing_library(binaryfd, senv);
+}
+
+ATF_TC_WITHOUT_HEAD(single_library);
+ATF_TC_BODY(single_library, tc)
+{
+	char *senv;
+
+	setup(tc);
+
+	ATF_REQUIRE(
+	    asprintf(&senv, "LD_PRELOAD_FDS=%d", libraryfd) > 0);
+
+	expect_success(binaryfd, senv);
+}
+
+ATF_TC_WITHOUT_HEAD(two_librarys);
+ATF_TC_BODY(two_librarys, tc)
+{
+	char *senv;
+
+	setup(tc);
+
+	ATF_REQUIRE(
+	    asprintf(&senv, "LD_PRELOAD_FDS=%d:%d", libraryfd, libraryfd) > 0);
+
+	expect_success(binaryfd, senv);
+}
+
+/* Register test cases with ATF. */
+ATF_TP_ADD_TCS(tp)
+{
+
+	ATF_TP_ADD_TC(tp, missing_library);
+	ATF_TP_ADD_TC(tp, bad_librarys);
+	ATF_TP_ADD_TC(tp, single_library);
+	ATF_TP_ADD_TC(tp, two_librarys);
+
+	return atf_no_error();
+}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202103242240.12OMeRHq089980>