aboutsummaryrefslogtreecommitdiff
path: root/elf
diff options
context:
space:
mode:
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>2025-02-18 15:58:16 -0500
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>2025-03-13 16:50:16 -0300
commit9b646f5dc933dfa019f2ed7f80b6198b43e31f62 (patch)
treef9f36d3010ff3ae93287aae0e945bfb8d23efba5 /elf
parentdded0d20f67ba1925ccbcb9cf28f0c75febe0dbe (diff)
downloadglibc-9b646f5dc933dfa019f2ed7f80b6198b43e31f62.tar.xz
glibc-9b646f5dc933dfa019f2ed7f80b6198b43e31f62.zip
elf: Canonicalize $ORIGIN in an explicit ld.so invocation [BZ 25263]
When an executable is invoked directly, we calculate $ORIGIN by calling readlink on /proc/self/exe, which the Linux kernel resolves to the target of any symlinks. However, if an executable is run through ld.so, we cannot use /proc/self/exe and instead use the path given as an argument. This leads to a different calculation of $ORIGIN, which is most notable in that it causes ldd to behave differently (e.g., by not finding a library) from directly running the program. To make the behavior consistent, take advantage of the fact that the kernel also resolves /proc/self/fd/ symlinks to the target of any symlinks in the same manner, so once we have opened the main executable in order to load it, replace the user-provided path with the result of calling readlink("/proc/self/fd/N"). (On non-Linux platforms this resolution does not happen and so no behavior change is needed.) The __fd_to_filename requires _fitoa_word and _itoa_word, which for 32-bits pulls a lot of definitions from _itoa.c (due _ITOA_NEEDED being defined). To simplify the build move the required function to a new file, _fitoa_word.c. Checked on x86_64-linux-gnu and i686-linux-gnu. Co-authored-by: Geoffrey Thomas <geofft@ldpreload.com> Reviewed-by: Geoffrey Thomas <geofft@ldpreload.com> Tested-by: Geoffrey Thomas <geofft@ldpreload.com>
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile24
-rw-r--r--elf/dl-load.c6
-rw-r--r--elf/dl-origin.c6
-rw-r--r--elf/liborigin-mod.c1
-rw-r--r--elf/tst-origin.c26
-rwxr-xr-xelf/tst-origin.sh60
6 files changed, 123 insertions, 0 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 77a76f2142..2bce1ed486 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -3442,3 +3442,27 @@ $(objpfx)tst-dlopen-constructor-null: \
$(objpfx)tst-dlopen-constructor-null-mod2.so
$(objpfx)tst-dlopen-constructor-null-mod2.so: \
$(objpfx)tst-dlopen-constructor-null-mod1.so
+
+ifeq ($(run-built-tests),yes)
+tests-special += $(objpfx)tst-origin.out
+endif
+CFLAGS-tst-origin.c += $(no-stack-protector)
+$(objpfx)tst-origin: $(objpfx)tst-origin.o $(objpfx)liborigin-mod.so
+ $(LINK.o) -o $@ -B$(csu-objpfx) $(LDFLAGS.so) $< \
+ -Wl,-rpath,\$$ORIGIN \
+ -L$(subst :, -L,$(rpath-link)) -Wl,--no-as-needed -lorigin-mod
+$(objpfx)liborigin-mod.so: $(objpfx)liborigin-mod.os
+ $(LINK.o) -shared -o $@ -B$(csu-objpfx) $(LDFLAGS.so) \
+ $(LDFLAGS-soname-fname) \
+ $<
+$(objpfx)tst-origin.out: tst-origin.sh $(objpfx)tst-origin
+ $(SHELL) \
+ $< \
+ '$(common-objpfx)' \
+ '$(test-wrapper-env)' \
+ '$(run-program-env)' \
+ '$(rpath-link)' \
+ tst-origin \
+ liborigin-mod.so \
+ > $@; \
+ $(evaluate-test)
diff --git a/elf/dl-load.c b/elf/dl-load.c
index 4998652adf..6b7e9799f3 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -965,6 +965,12 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
{
assert (nsid == LM_ID_BASE);
memset (&id, 0, sizeof (id));
+ char *realname_can = _dl_canonicalize (fd);
+ if (realname_can != NULL)
+ {
+ free (realname);
+ realname = realname_can;
+ }
}
else
{
diff --git a/elf/dl-origin.c b/elf/dl-origin.c
index 9f6b921b01..812f5dbb28 100644
--- a/elf/dl-origin.c
+++ b/elf/dl-origin.c
@@ -47,3 +47,9 @@ _dl_get_origin (void)
return result;
}
+
+char *
+_dl_canonicalize (int fd)
+{
+ return NULL;
+}
diff --git a/elf/liborigin-mod.c b/elf/liborigin-mod.c
new file mode 100644
index 0000000000..aa6d4c27df
--- /dev/null
+++ b/elf/liborigin-mod.c
@@ -0,0 +1 @@
+void foo (void) {}
diff --git a/elf/tst-origin.c b/elf/tst-origin.c
new file mode 100644
index 0000000000..734b2e81f6
--- /dev/null
+++ b/elf/tst-origin.c
@@ -0,0 +1,26 @@
+/* Test if $ORIGIN works correctly with symlinks (BZ 25263)
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+extern void foo (void);
+
+int
+main (int argc, char *argv[])
+{
+ foo ();
+ return 0;
+}
diff --git a/elf/tst-origin.sh b/elf/tst-origin.sh
new file mode 100755
index 0000000000..a033810bac
--- /dev/null
+++ b/elf/tst-origin.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+# Test if $ORIGIN works correctly with symlinks (BZ 25263)
+# Copyright (C) 2025 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <https://www.gnu.org/licenses/>.
+
+set -e
+
+objpfx=$1
+test_wrapper_env=$2
+run_program_env=$3
+library_path=$4
+test_program=$5
+test_library=$6
+
+cleanup()
+{
+ # Move the binary and library back to build directory
+ mv $tmpdir/sub/$test_program ${objpfx}elf
+ mv $tmpdir/sub/$test_library ${objpfx}elf
+
+ rm -rf $tmpdir
+}
+
+tmpdir=$(mktemp -d "${objpfx}elf/tst-origin.XXXXXXXXXX")
+trap cleanup 0
+
+mkdir ${tmpdir}/sub
+
+# Remove the dependency from $library_path
+mv ${objpfx}elf/$test_program $tmpdir/sub
+mv ${objpfx}elf/$test_library $tmpdir/sub
+
+cd ${tmpdir}
+ln -s sub/$test_program $test_program
+
+${test_wrapper_env} \
+${run_program_env} \
+${objpfx}elf/ld.so --library-path "$library_path" \
+ ./$test_program 2>&1 && rc=0 || rc=$?
+
+# Also check if ldd resolves the dependency
+LD_TRACE_LOADED_OBJECTS=1 \
+${objpfx}elf/ld.so --library-path "$library_path" \
+ ./$test_program 2>&1 | grep 'not found' && rc=1 || rc=0
+
+exit $rc