diff --git a/libexec/rtld-elf/rtld.1 b/libexec/rtld-elf/rtld.1 --- 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 @@ 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 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -119,7 +119,7 @@ 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_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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ 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 --- 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 diff --git a/libexec/rtld-elf/tests/common.h b/libexec/rtld-elf/tests/common.h new file mode 100644 --- /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 + * + * 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/common.c b/libexec/rtld-elf/tests/common.c new file mode 100644 --- /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 + * + * 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 +#include +#include + +#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/ld_library_pathfds.c b/libexec/rtld-elf/tests/ld_library_pathfds.c --- a/libexec/rtld-elf/tests/ld_library_pathfds.c +++ b/libexec/rtld-elf/tests/ld_library_pathfds.c @@ -29,6 +29,7 @@ #include #include +#include "common.h" struct descriptors { int binary; @@ -38,14 +39,8 @@ 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 @@ 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 --- /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 + * + * 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 +#include +#include + +#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(); +}