]>
Commit | Line | Data |
---|---|---|
fe875de8 MT |
1 | commit 7cbcdb3699584db8913ca90f705d6337633ee10f |
2 | Author: Siddhesh Poyarekar <siddhesh@redhat.com> | |
3 | Date: Fri Oct 25 10:22:12 2013 +0530 | |
4 | ||
5 | Fix stack overflow due to large AF_INET6 requests | |
6 | ||
7 | Resolves #16072 (CVE-2013-4458). | |
8 | ||
9 | This patch fixes another stack overflow in getaddrinfo when it is | |
10 | called with AF_INET6. The AF_UNSPEC case was fixed as CVE-2013-1914, | |
11 | but the AF_INET6 case went undetected back then. | |
12 | ||
13 | commit 91ce40854d0b7f865cf5024ef95a8026b76096f3 | |
14 | Author: Florian Weimer <fweimer@redhat.com> | |
15 | Date: Fri Aug 16 09:38:52 2013 +0200 | |
16 | ||
17 | CVE-2013-4237, BZ #14699: Buffer overflow in readdir_r | |
18 | ||
19 | * sysdeps/posix/dirstream.h (struct __dirstream): Add errcode | |
20 | member. | |
21 | * sysdeps/posix/opendir.c (__alloc_dir): Initialize errcode | |
22 | member. | |
23 | * sysdeps/posix/rewinddir.c (rewinddir): Reset errcode member. | |
24 | * sysdeps/posix/readdir_r.c (__READDIR_R): Enforce NAME_MAX limit. | |
25 | Return delayed error code. Remove GETDENTS_64BIT_ALIGNED | |
26 | conditional. | |
27 | * sysdeps/unix/sysv/linux/wordsize-64/readdir_r.c: Do not define | |
28 | GETDENTS_64BIT_ALIGNED. | |
29 | * sysdeps/unix/sysv/linux/i386/readdir64_r.c: Likewise. | |
30 | * manual/filesys.texi (Reading/Closing Directory): Document | |
31 | ENAMETOOLONG return value of readdir_r. Recommend readdir more | |
32 | strongly. | |
33 | * manual/conf.texi (Limits for Files): Add portability note to | |
34 | NAME_MAX, PATH_MAX. | |
35 | (Pathconf): Add portability note for _PC_NAME_MAX, _PC_PATH_MAX. | |
36 | ||
37 | diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c | |
38 | index e6ce4cf..8ff74b4 100644 | |
39 | --- a/sysdeps/posix/getaddrinfo.c | |
40 | +++ b/sysdeps/posix/getaddrinfo.c | |
41 | @@ -197,7 +197,22 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, | |
42 | &rc, &herrno, NULL, &localcanon)); \ | |
43 | if (rc != ERANGE || herrno != NETDB_INTERNAL) \ | |
44 | break; \ | |
45 | - tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \ | |
46 | + if (!malloc_tmpbuf && __libc_use_alloca (alloca_used + 2 * tmpbuflen)) \ | |
47 | + tmpbuf = extend_alloca_account (tmpbuf, tmpbuflen, 2 * tmpbuflen, \ | |
48 | + alloca_used); \ | |
49 | + else \ | |
50 | + { \ | |
51 | + char *newp = realloc (malloc_tmpbuf ? tmpbuf : NULL, \ | |
52 | + 2 * tmpbuflen); \ | |
53 | + if (newp == NULL) \ | |
54 | + { \ | |
55 | + result = -EAI_MEMORY; \ | |
56 | + goto free_and_return; \ | |
57 | + } \ | |
58 | + tmpbuf = newp; \ | |
59 | + malloc_tmpbuf = true; \ | |
60 | + tmpbuflen = 2 * tmpbuflen; \ | |
61 | + } \ | |
62 | } \ | |
63 | if (status == NSS_STATUS_SUCCESS && rc == 0) \ | |
64 | h = &th; \ | |
65 | @@ -209,7 +224,8 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp, | |
66 | { \ | |
67 | __set_h_errno (herrno); \ | |
68 | _res.options = old_res_options; \ | |
69 | - return -EAI_SYSTEM; \ | |
70 | + result = -EAI_SYSTEM; \ | |
71 | + goto free_and_return; \ | |
72 | } \ | |
73 | if (herrno == TRY_AGAIN) \ | |
74 | no_data = EAI_AGAIN; \ | |
75 | ||
76 | diff --git a/manual/conf.texi b/manual/conf.texi | |
77 | index 7eb8b36..c720063 100644 | |
78 | --- a/manual/conf.texi | |
79 | +++ b/manual/conf.texi | |
80 | @@ -1149,6 +1149,9 @@ typed ahead as input. @xref{I/O Queues}. | |
81 | @comment POSIX.1 | |
82 | @deftypevr Macro int NAME_MAX | |
83 | The uniform system limit (if any) for the length of a file name component. | |
84 | + | |
85 | +@strong{Portability Note:} On some systems, the GNU C Library defines | |
86 | +@code{NAME_MAX}, but does not actually enforce this limit. | |
87 | @end deftypevr | |
88 | ||
89 | @comment limits.h | |
90 | @@ -1157,6 +1160,9 @@ including the terminating null character. | |
91 | @deftypevr Macro int PATH_MAX | |
92 | The uniform system limit (if any) for the length of an entire file name (that | |
93 | is, the argument given to system calls such as @code{open}). | |
94 | + | |
95 | +@strong{Portability Note:} The GNU C Library does not enforce this limit | |
96 | +even if @code{PATH_MAX} is defined. | |
97 | @end deftypevr | |
98 | ||
99 | @cindex limits, pipe buffer size | |
100 | @@ -1476,6 +1482,9 @@ Inquire about the value of @code{POSIX_REC_MIN_XFER_SIZE}. | |
101 | Inquire about the value of @code{POSIX_REC_XFER_ALIGN}. | |
102 | @end table | |
103 | ||
104 | +@strong{Portability Note:} On some systems, the GNU C Library does not | |
105 | +enforce @code{_PC_NAME_MAX} or @code{_PC_PATH_MAX} limits. | |
106 | + | |
107 | @node Utility Limits | |
108 | @section Utility Program Capacity Limits | |
109 | ||
110 | diff --git a/manual/filesys.texi b/manual/filesys.texi | |
111 | index 1df9cf2..814c210 100644 | |
112 | --- a/manual/filesys.texi | |
113 | +++ b/manual/filesys.texi | |
114 | @@ -444,9 +444,9 @@ symbols are declared in the header file @file{dirent.h}. | |
115 | @comment POSIX.1 | |
116 | @deftypefun {struct dirent *} readdir (DIR *@var{dirstream}) | |
117 | This function reads the next entry from the directory. It normally | |
118 | -returns a pointer to a structure containing information about the file. | |
119 | -This structure is statically allocated and can be rewritten by a | |
120 | -subsequent call. | |
121 | +returns a pointer to a structure containing information about the | |
122 | +file. This structure is associated with the @var{dirstream} handle | |
123 | +and can be rewritten by a subsequent call. | |
124 | ||
125 | @strong{Portability Note:} On some systems @code{readdir} may not | |
126 | return entries for @file{.} and @file{..}, even though these are always | |
127 | @@ -461,19 +461,61 @@ conditions are defined for this function: | |
128 | The @var{dirstream} argument is not valid. | |
129 | @end table | |
130 | ||
131 | -@code{readdir} is not thread safe. Multiple threads using | |
132 | -@code{readdir} on the same @var{dirstream} may overwrite the return | |
133 | -value. Use @code{readdir_r} when this is critical. | |
134 | +To distinguish between an end-of-directory condition or an error, you | |
135 | +must set @code{errno} to zero before calling @code{readdir}. To avoid | |
136 | +entering an infinite loop, you should stop reading from the directory | |
137 | +after the first error. | |
138 | + | |
139 | +In POSIX.1-2008, @code{readdir} is not thread-safe. In the GNU C Library | |
140 | +implementation, it is safe to call @code{readdir} concurrently on | |
141 | +different @var{dirstream}s, but multiple threads accessing the same | |
142 | +@var{dirstream} result in undefined behavior. @code{readdir_r} is a | |
143 | +fully thread-safe alternative, but suffers from poor portability (see | |
144 | +below). It is recommended that you use @code{readdir}, with external | |
145 | +locking if multiple threads access the same @var{dirstream}. | |
146 | @end deftypefun | |
147 | ||
148 | @comment dirent.h | |
149 | @comment GNU | |
150 | @deftypefun int readdir_r (DIR *@var{dirstream}, struct dirent *@var{entry}, struct dirent **@var{result}) | |
151 | -This function is the reentrant version of @code{readdir}. Like | |
152 | -@code{readdir} it returns the next entry from the directory. But to | |
153 | -prevent conflicts between simultaneously running threads the result is | |
154 | -not stored in statically allocated memory. Instead the argument | |
155 | -@var{entry} points to a place to store the result. | |
156 | +This function is a version of @code{readdir} which performs internal | |
157 | +locking. Like @code{readdir} it returns the next entry from the | |
158 | +directory. To prevent conflicts between simultaneously running | |
159 | +threads the result is stored inside the @var{entry} object. | |
160 | + | |
161 | +@strong{Portability Note:} It is recommended to use @code{readdir} | |
162 | +instead of @code{readdir_r} for the following reasons: | |
163 | + | |
164 | +@itemize @bullet | |
165 | +@item | |
166 | +On systems which do not define @code{NAME_MAX}, it may not be possible | |
167 | +to use @code{readdir_r} safely because the caller does not specify the | |
168 | +length of the buffer for the directory entry. | |
169 | + | |
170 | +@item | |
171 | +On some systems, @code{readdir_r} cannot read directory entries with | |
172 | +very long names. If such a name is encountered, the GNU C Library | |
173 | +implementation of @code{readdir_r} returns with an error code of | |
174 | +@code{ENAMETOOLONG} after the final directory entry has been read. On | |
175 | +other systems, @code{readdir_r} may return successfully, but the | |
176 | +@code{d_name} member may not be NUL-terminated or may be truncated. | |
177 | + | |
178 | +@item | |
179 | +POSIX-1.2008 does not guarantee that @code{readdir} is thread-safe, | |
180 | +even when access to the same @var{dirstream} is serialized. But in | |
181 | +current implementations (including the GNU C Library), it is safe to call | |
182 | +@code{readdir} concurrently on different @var{dirstream}s, so there is | |
183 | +no need to use @code{readdir_r} in most multi-threaded programs. In | |
184 | +the rare case that multiple threads need to read from the same | |
185 | +@var{dirstream}, it is still better to use @code{readdir} and external | |
186 | +synchronization. | |
187 | + | |
188 | +@item | |
189 | +It is expected that future versions of POSIX will obsolete | |
190 | +@code{readdir_r} and mandate the level of thread safety for | |
191 | +@code{readdir} which is provided by the GNU C Library and other | |
192 | +implementations today. | |
193 | +@end itemize | |
194 | ||
195 | Normally @code{readdir_r} returns zero and sets @code{*@var{result}} | |
196 | to @var{entry}. If there are no more entries in the directory or an | |
197 | @@ -481,15 +523,6 @@ error is detected, @code{readdir_r} sets @code{*@var{result}} to a | |
198 | null pointer and returns a nonzero error code, also stored in | |
199 | @code{errno}, as described for @code{readdir}. | |
200 | ||
201 | -@strong{Portability Note:} On some systems @code{readdir_r} may not | |
202 | -return a NUL terminated string for the file name, even when there is no | |
203 | -@code{d_reclen} field in @code{struct dirent} and the file | |
204 | -name is the maximum allowed size. Modern systems all have the | |
205 | -@code{d_reclen} field, and on old systems multi-threading is not | |
206 | -critical. In any case there is no such problem with the @code{readdir} | |
207 | -function, so that even on systems without the @code{d_reclen} member one | |
208 | -could use multiple threads by using external locking. | |
209 | - | |
210 | It is also important to look at the definition of the @code{struct | |
211 | dirent} type. Simply passing a pointer to an object of this type for | |
212 | the second parameter of @code{readdir_r} might not be enough. Some | |
213 | diff --git a/sysdeps/unix/dirstream.h b/sysdeps/unix/dirstream.h | |
214 | index a7a074d..8e8570d 100644 | |
215 | --- a/sysdeps/unix/dirstream.h | |
216 | +++ b/sysdeps/unix/dirstream.h | |
217 | @@ -39,6 +39,8 @@ struct __dirstream | |
218 | ||
219 | off_t filepos; /* Position of next entry to read. */ | |
220 | ||
221 | + int errcode; /* Delayed error code. */ | |
222 | + | |
223 | /* Directory block. */ | |
224 | char data[0] __attribute__ ((aligned (__alignof__ (void*)))); | |
225 | }; | |
226 | diff --git a/sysdeps/unix/opendir.c b/sysdeps/unix/opendir.c | |
227 | index ddfc3a7..fc05b0f 100644 | |
228 | --- a/sysdeps/unix/opendir.c | |
229 | +++ b/sysdeps/unix/opendir.c | |
230 | @@ -231,6 +231,7 @@ __alloc_dir (int fd, bool close_fd, int flags, const struct stat64 *statp) | |
231 | dirp->size = 0; | |
232 | dirp->offset = 0; | |
233 | dirp->filepos = 0; | |
234 | + dirp->errcode = 0; | |
235 | ||
236 | return dirp; | |
237 | } | |
238 | diff --git a/sysdeps/unix/readdir_r.c b/sysdeps/unix/readdir_r.c | |
239 | index b5a8e2e..8ed5c3f 100644 | |
240 | --- a/sysdeps/unix/readdir_r.c | |
241 | +++ b/sysdeps/unix/readdir_r.c | |
242 | @@ -40,6 +40,7 @@ __READDIR_R (DIR *dirp, DIRENT_TYPE *entry, DIRENT_TYPE **result) | |
243 | DIRENT_TYPE *dp; | |
244 | size_t reclen; | |
245 | const int saved_errno = errno; | |
246 | + int ret; | |
247 | ||
248 | __libc_lock_lock (dirp->lock); | |
249 | ||
250 | @@ -70,10 +71,10 @@ __READDIR_R (DIR *dirp, DIRENT_TYPE *entry, DIRENT_TYPE **result) | |
251 | bytes = 0; | |
252 | __set_errno (saved_errno); | |
253 | } | |
254 | + if (bytes < 0) | |
255 | + dirp->errcode = errno; | |
256 | ||
257 | dp = NULL; | |
258 | - /* Reclen != 0 signals that an error occurred. */ | |
259 | - reclen = bytes != 0; | |
260 | break; | |
261 | } | |
262 | dirp->size = (size_t) bytes; | |
263 | @@ -106,28 +107,46 @@ __READDIR_R (DIR *dirp, DIRENT_TYPE *entry, DIRENT_TYPE **result) | |
264 | dirp->filepos += reclen; | |
265 | #endif | |
266 | ||
267 | - /* Skip deleted files. */ | |
268 | +#ifdef NAME_MAX | |
269 | + if (reclen > offsetof (DIRENT_TYPE, d_name) + NAME_MAX + 1) | |
270 | + { | |
271 | + /* The record is very long. It could still fit into the | |
272 | + caller-supplied buffer if we can skip padding at the | |
273 | + end. */ | |
274 | + size_t namelen = _D_EXACT_NAMLEN (dp); | |
275 | + if (namelen <= NAME_MAX) | |
276 | + reclen = offsetof (DIRENT_TYPE, d_name) + namelen + 1; | |
277 | + else | |
278 | + { | |
279 | + /* The name is too long. Ignore this file. */ | |
280 | + dirp->errcode = ENAMETOOLONG; | |
281 | + dp->d_ino = 0; | |
282 | + continue; | |
283 | + } | |
284 | + } | |
285 | +#endif | |
286 | + | |
287 | + /* Skip deleted and ignored files. */ | |
288 | } | |
289 | while (dp->d_ino == 0); | |
290 | ||
291 | if (dp != NULL) | |
292 | { | |
293 | -#ifdef GETDENTS_64BIT_ALIGNED | |
294 | - /* The d_reclen value might include padding which is not part of | |
295 | - the DIRENT_TYPE data structure. */ | |
296 | - reclen = MIN (reclen, sizeof (DIRENT_TYPE)); | |
297 | -#endif | |
298 | *result = memcpy (entry, dp, reclen); | |
299 | -#ifdef GETDENTS_64BIT_ALIGNED | |
300 | +#ifdef _DIRENT_HAVE_D_RECLEN | |
301 | entry->d_reclen = reclen; | |
302 | #endif | |
303 | + ret = 0; | |
304 | } | |
305 | else | |
306 | - *result = NULL; | |
307 | + { | |
308 | + *result = NULL; | |
309 | + ret = dirp->errcode; | |
310 | + } | |
311 | ||
312 | __libc_lock_unlock (dirp->lock); | |
313 | ||
314 | - return dp != NULL ? 0 : reclen ? errno : 0; | |
315 | + return ret; | |
316 | } | |
317 | ||
318 | #ifdef __READDIR_R_ALIAS | |
319 | diff --git a/sysdeps/unix/rewinddir.c b/sysdeps/unix/rewinddir.c | |
320 | index 2935a8e..d4991ad 100644 | |
321 | --- a/sysdeps/unix/rewinddir.c | |
322 | +++ b/sysdeps/unix/rewinddir.c | |
323 | @@ -33,5 +33,6 @@ rewinddir (dirp) | |
324 | dirp->filepos = 0; | |
325 | dirp->offset = 0; | |
326 | dirp->size = 0; | |
327 | + dirp->errcode = 0; | |
328 | __libc_lock_unlock (dirp->lock); | |
329 | } | |
330 | diff --git a/sysdeps/unix/sysv/linux/i386/readdir64_r.c b/sysdeps/unix/sysv/linux/i386/readdir64_r.c | |
331 | index 8ebbcfd..a7d114e 100644 | |
332 | --- a/sysdeps/unix/sysv/linux/i386/readdir64_r.c | |
333 | +++ b/sysdeps/unix/sysv/linux/i386/readdir64_r.c | |
334 | @@ -18,7 +18,6 @@ | |
335 | #define __READDIR_R __readdir64_r | |
336 | #define __GETDENTS __getdents64 | |
337 | #define DIRENT_TYPE struct dirent64 | |
338 | -#define GETDENTS_64BIT_ALIGNED 1 | |
339 | ||
340 | #include <sysdeps/unix/readdir_r.c> | |
341 |