[PATCH 1/1] elf: Canonicalize $ORIGIN in an explicit ld.so invocation [BZ #25263]
Geoffrey Thomas
geofft@ldpreload.com
Tue Feb 18 20:58:16 GMT 2025
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.)
---
elf/dl-load.c | 1 +
elf/dl-origin.c | 5 +++++
sysdeps/generic/ldsodefs.h | 5 +++++
sysdeps/unix/sysv/linux/dl-origin.c | 27 +++++++++++++++++++++++++++
4 files changed, 38 insertions(+)
diff --git a/elf/dl-load.c b/elf/dl-load.c
index f905578a65..2ef585bc5c 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -965,6 +965,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
{
assert (nsid == LM_ID_BASE);
memset (&id, 0, sizeof (id));
+ _dl_canonicalize (&realname, fd);
}
else
{
diff --git a/elf/dl-origin.c b/elf/dl-origin.c
index 9f6b921b01..d942f3f0bb 100644
--- a/elf/dl-origin.c
+++ b/elf/dl-origin.c
@@ -47,3 +47,8 @@ _dl_get_origin (void)
return result;
}
+
+void
+_dl_canonicalize (char **filename, int fd)
+{
+}
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index e871f27ff2..36b472e19c 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -1198,6 +1198,11 @@ extern struct link_map * _dl_get_dl_main_map (void) attribute_hidden;
/* Find origin of the executable. */
extern const char *_dl_get_origin (void) attribute_hidden;
+/* Canonicalize the path to an open file. FILENAME is a pointer to a
+ string allocated with malloc; it may be freed and replaced with
+ another string allocated with malloc. */
+extern void _dl_canonicalize (char **filename, int fd) attribute_hidden;
+
/* Count DSTs. */
extern size_t _dl_dst_count (const char *name) attribute_hidden;
diff --git a/sysdeps/unix/sysv/linux/dl-origin.c b/sysdeps/unix/sysv/linux/dl-origin.c
index decdd8ae9e..b12eff4315 100644
--- a/sysdeps/unix/sysv/linux/dl-origin.c
+++ b/sysdeps/unix/sysv/linux/dl-origin.c
@@ -72,3 +72,30 @@ _dl_get_origin (void)
return result;
}
+
+/* On Linux, readlink on the magic symlinks in /proc/self/fd also has
+ the same behavior of returning the canonical path from the dcache.
+ If it does not work, we do not bother to canonicalize. */
+
+void
+_dl_canonicalize (char **filename, int fd)
+{
+ char *canonical = (char *) malloc (PATH_MAX + 1);
+ char buf[25];
+ buf[24] = '\0';
+ char *path = _itoa (fd, buf + 24, 10, 0);
+ path = memcpy (path - 14, "/proc/self/fd/", 14);
+
+ int size = INTERNAL_SYSCALL_CALL (readlinkat, AT_FDCWD, path,
+ canonical, PATH_MAX );
+ if (size >= 0)
+ {
+ free (*filename);
+ canonical[size] = '\0';
+ *filename = canonical;
+ }
+ else
+ {
+ free (canonical);
+ }
+}
--
2.39.5 (Apple Git-154)
More information about the Libc-alpha
mailing list