![Peter Korsgaard](/assets/img/avatar_default.png)
glibc contains a vulnerability that allows specially crafted LD_LIBRARY_PATH values to manipulate the heap/stack, causing them to alias, potentially resulting in arbitrary code execution. Please note that additional hardening changes have been made to glibc to prevent manipulation of stack and heap memory but these issues are not directly exploitable, as such they have not been given a CVE. https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt Patches are identical to upstream, except that the ChangeLog modifications have been stripped. Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
123 lines
3.8 KiB
Diff
123 lines
3.8 KiB
Diff
From 6d0ba622891bed9d8394eef1935add53003b12e8 Mon Sep 17 00:00:00 2001
|
|
From: Florian Weimer <fweimer@redhat.com>
|
|
Date: Mon, 19 Jun 2017 22:31:04 +0200
|
|
Subject: [PATCH] ld.so: Reject overly long LD_PRELOAD path elements
|
|
|
|
[Peter: Drop ChangeLog modification]
|
|
Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
|
|
---
|
|
elf/rtld.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++------------
|
|
1 file changed, 72 insertions(+), 16 deletions(-)
|
|
|
|
diff --git a/elf/rtld.c b/elf/rtld.c
|
|
index 2269dbec81..86ae20c83f 100644
|
|
--- a/elf/rtld.c
|
|
+++ b/elf/rtld.c
|
|
@@ -99,6 +99,35 @@ uintptr_t __pointer_chk_guard_local
|
|
strong_alias (__pointer_chk_guard_local, __pointer_chk_guard)
|
|
#endif
|
|
|
|
+/* Length limits for names and paths, to protect the dynamic linker,
|
|
+ particularly when __libc_enable_secure is active. */
|
|
+#ifdef NAME_MAX
|
|
+# define SECURE_NAME_LIMIT NAME_MAX
|
|
+#else
|
|
+# define SECURE_NAME_LIMIT 255
|
|
+#endif
|
|
+#ifdef PATH_MAX
|
|
+# define SECURE_PATH_LIMIT PATH_MAX
|
|
+#else
|
|
+# define SECURE_PATH_LIMIT 1024
|
|
+#endif
|
|
+
|
|
+/* Check that AT_SECURE=0, or that the passed name does not contain
|
|
+ directories and is not overly long. Reject empty names
|
|
+ unconditionally. */
|
|
+static bool
|
|
+dso_name_valid_for_suid (const char *p)
|
|
+{
|
|
+ if (__glibc_unlikely (__libc_enable_secure))
|
|
+ {
|
|
+ /* Ignore pathnames with directories for AT_SECURE=1
|
|
+ programs, and also skip overlong names. */
|
|
+ size_t len = strlen (p);
|
|
+ if (len >= SECURE_NAME_LIMIT || memchr (p, '/', len) != NULL)
|
|
+ return false;
|
|
+ }
|
|
+ return *p != '\0';
|
|
+}
|
|
|
|
/* List of auditing DSOs. */
|
|
static struct audit_list
|
|
@@ -718,6 +747,42 @@ static const char *preloadlist attribute_relro;
|
|
/* Nonzero if information about versions has to be printed. */
|
|
static int version_info attribute_relro;
|
|
|
|
+/* The LD_PRELOAD environment variable gives list of libraries
|
|
+ separated by white space or colons that are loaded before the
|
|
+ executable's dependencies and prepended to the global scope list.
|
|
+ (If the binary is running setuid all elements containing a '/' are
|
|
+ ignored since it is insecure.) Return the number of preloads
|
|
+ performed. */
|
|
+unsigned int
|
|
+handle_ld_preload (const char *preloadlist, struct link_map *main_map)
|
|
+{
|
|
+ unsigned int npreloads = 0;
|
|
+ const char *p = preloadlist;
|
|
+ char fname[SECURE_PATH_LIMIT];
|
|
+
|
|
+ while (*p != '\0')
|
|
+ {
|
|
+ /* Split preload list at space/colon. */
|
|
+ size_t len = strcspn (p, " :");
|
|
+ if (len > 0 && len < sizeof (fname))
|
|
+ {
|
|
+ memcpy (fname, p, len);
|
|
+ fname[len] = '\0';
|
|
+ }
|
|
+ else
|
|
+ fname[0] = '\0';
|
|
+
|
|
+ /* Skip over the substring and the following delimiter. */
|
|
+ p += len;
|
|
+ if (*p != '\0')
|
|
+ ++p;
|
|
+
|
|
+ if (dso_name_valid_for_suid (fname))
|
|
+ npreloads += do_preload (fname, main_map, "LD_PRELOAD");
|
|
+ }
|
|
+ return npreloads;
|
|
+}
|
|
+
|
|
static void
|
|
dl_main (const ElfW(Phdr) *phdr,
|
|
ElfW(Word) phnum,
|
|
@@ -1464,23 +1529,8 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
|
|
|
|
if (__glibc_unlikely (preloadlist != NULL))
|
|
{
|
|
- /* The LD_PRELOAD environment variable gives list of libraries
|
|
- separated by white space or colons that are loaded before the
|
|
- executable's dependencies and prepended to the global scope
|
|
- list. If the binary is running setuid all elements
|
|
- containing a '/' are ignored since it is insecure. */
|
|
- char *list = strdupa (preloadlist);
|
|
- char *p;
|
|
-
|
|
HP_TIMING_NOW (start);
|
|
-
|
|
- /* Prevent optimizing strsep. Speed is not important here. */
|
|
- while ((p = (strsep) (&list, " :")) != NULL)
|
|
- if (p[0] != '\0'
|
|
- && (__builtin_expect (! __libc_enable_secure, 1)
|
|
- || strchr (p, '/') == NULL))
|
|
- npreloads += do_preload (p, main_map, "LD_PRELOAD");
|
|
-
|
|
+ npreloads += handle_ld_preload (preloadlist, main_map);
|
|
HP_TIMING_NOW (stop);
|
|
HP_TIMING_DIFF (diff, start, stop);
|
|
HP_TIMING_ACCUM_NT (load_time, diff);
|
|
--
|
|
2.11.0
|
|
|