[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