1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
From 46703a3995aa3ca2b816814aa4ad05ed524194dd 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
(cherry picked from commit 6d0ba622891bed9d8394eef1935add53003b12e8)
diff --git a/elf/rtld.c b/elf/rtld.c
index 2fc33a6..4badcf6 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
@@ -716,6 +745,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,
@@ -1462,23 +1527,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.9.3
|