summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2024-01-14 09:39:04 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2024-01-14 09:39:04 +0000
commit58bc31051a83b6f73ab5cedccc2d7c02cd9431ad (patch)
tree7132806122f86d6d3d0bb9b6336d4d4b944c0fb4
parent6e2f4b48cec0dca9241d3a2f8ca63dcfe1c37df0 (diff)
Whenever we have a libc major bump, we run the risk that dependent shared
libraries will request a different (major) libc version from the one requested by the binary itself. For various reasons loading multiple libc versions is not a good idea, and since the introduction of msyscall(2) support, system calls will only work when called from one of the two loaded libcs. This really means that when we have a libc major bump, users must update all dynamic executables and shared libraries in the system. However, to ease this transition, change ld.so to only load the first libc version that we encounter (in a breadth first sense) and substitute that libc version for all further loads of libc, even if different versions are requested. This is done silently since I can't come up with a good warning message. In practice this means the libc version requested by the executable itself will be loaded. This means that shared libraries may fail to load if they use a symbol that has been removed. But given the constraints, this is the best that we can do. Even when we bump the libc major, the set of changes is typically small and most binaries and shared libraries will continue to run and allow the user to run pkg_add -u without any fallout. ok deraadt@, gkoehler@
-rw-r--r--libexec/ld.so/loader.c32
1 files changed, 31 insertions, 1 deletions
diff --git a/libexec/ld.so/loader.c b/libexec/ld.so/loader.c
index 9f15530e2c4..de53dc85d58 100644
--- a/libexec/ld.so/loader.c
+++ b/libexec/ld.so/loader.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: loader.c,v 1.218 2023/12/19 16:13:22 deraadt Exp $ */
+/* $OpenBSD: loader.c,v 1.219 2024/01/14 09:39:03 kettenis Exp $ */
/*
* Copyright (c) 1998 Per Fogelstrom, Opsycon AB
@@ -70,6 +70,7 @@ int _dl_trust __relro = 0;
char **_dl_libpath __relro = NULL;
const char **_dl_argv __relro = NULL;
int _dl_argc __relro = 0;
+const char *_dl_libcname;
char *_dl_preload __boot_data = NULL;
char *_dl_tracefmt1 __boot_data = NULL;
@@ -358,6 +359,31 @@ _dl_load_dep_libs(elf_object_t *object, int flags, int booting)
if (dynp->d_tag == DT_NEEDED)
liblist[loop++].dynp = dynp;
+ /*
+ * We can't support multiple versions of libc
+ * in a single process. So remember the first
+ * libc SONAME we encounter as a dependency
+ * and use it in further loads of libc. In
+ * practice this means we will always use the
+ * libc version that the binary was linked
+ * against. This isn't entirely correct, but
+ * it will keep most binaries running when
+ * transitioning over a libc major bump.
+ */
+ if (_dl_libcname == NULL) {
+ for (loop = 0; loop < libcount; loop++) {
+ const char *libname;
+ libname = dynobj->dyn.strtab;
+ libname +=
+ liblist[loop].dynp->d_un.d_val;
+ if (_dl_strncmp(libname,
+ "libc.so.", 8) == 0) {
+ _dl_libcname = libname;
+ break;
+ }
+ }
+ }
+
/* Randomize these */
for (loop = 0; loop < libcount; loop++)
randomlist[loop] = loop;
@@ -380,6 +406,10 @@ _dl_load_dep_libs(elf_object_t *object, int flags, int booting)
liblist[randomlist[loop]].dynp->d_un.d_val;
DL_DEB(("loading: %s required by %s\n", libname,
dynobj->load_name));
+ if (_dl_strncmp(libname, "libc.so.", 8) == 0) {
+ if (_dl_libcname)
+ libname = _dl_libcname;
+ }
depobj = _dl_load_shlib(libname, dynobj,
OBJTYPE_LIB, depflags, nodelete);
if (depobj == 0) {