aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert2017-10-09 10:30:40 -0700
committerPaul Eggert2017-10-09 10:48:55 -0700
commit934f08f3de7929e54e9da3271a34bb49a8854f2c (patch)
tree99f898f4f6242d35d697749957ca41e813054ca7
parent6c2b1e89efe90e1b55bc0f0296e68efa52dbcbc1 (diff)
downloademacs-934f08f3de7929e54e9da3271a34bb49a8854f2c.tar.gz
emacs-934f08f3de7929e54e9da3271a34bb49a8854f2c.zip
Fix unlikely overflows with wd length
* src/sysdep.c (get_current_dir_name_or_unreachable): Avoid integer overflow if working directory name is absurdly long. When allocating memory for getcwd, do not exceed MAXPATHLEN.
-rw-r--r--src/sysdep.c57
1 files changed, 39 insertions, 18 deletions
diff --git a/src/sysdep.c b/src/sysdep.c
index c3484920d0c..6d24b7fa2b1 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -228,6 +228,22 @@ init_standard_fds (void)
228static char * 228static char *
229get_current_dir_name_or_unreachable (void) 229get_current_dir_name_or_unreachable (void)
230{ 230{
231 /* Use malloc, not xmalloc, since this function can be called before
232 the xmalloc exception machinery is available. */
233
234 char *pwd;
235
236 /* The maximum size of a directory name, including the terminating null.
237 Leave room so that the caller can append a trailing slash. */
238 ptrdiff_t dirsize_max = min (PTRDIFF_MAX, SIZE_MAX) - 1;
239
240 /* The maximum size of a buffer for a file name, including the
241 terminating null. This is bounded by MAXPATHLEN, if available. */
242 ptrdiff_t bufsize_max = dirsize_max;
243#ifdef MAXPATHLEN
244 bufsize_max = min (bufsize_max, MAXPATHLEN);
245#endif
246
231# if HAVE_GET_CURRENT_DIR_NAME && !BROKEN_GET_CURRENT_DIR_NAME 247# if HAVE_GET_CURRENT_DIR_NAME && !BROKEN_GET_CURRENT_DIR_NAME
232# ifdef HYBRID_MALLOC 248# ifdef HYBRID_MALLOC
233 bool use_libc = bss_sbrk_did_unexec; 249 bool use_libc = bss_sbrk_did_unexec;
@@ -238,56 +254,61 @@ get_current_dir_name_or_unreachable (void)
238 { 254 {
239 /* For an unreachable directory, this returns a string that starts 255 /* For an unreachable directory, this returns a string that starts
240 with "(unreachable)"; see Bug#27871. */ 256 with "(unreachable)"; see Bug#27871. */
241 return get_current_dir_name (); 257 pwd = get_current_dir_name ();
258 if (pwd)
259 {
260 if (strlen (pwd) < dirsize_max)
261 return pwd;
262 free (pwd);
263 errno = ERANGE;
264 }
265 return NULL;
242 } 266 }
243# endif 267# endif
244 268
245 char *buf; 269 size_t pwdlen;
246 char *pwd = getenv ("PWD");
247 struct stat dotstat, pwdstat; 270 struct stat dotstat, pwdstat;
271 pwd = getenv ("PWD");
272
248 /* If PWD is accurate, use it instead of calling getcwd. PWD is 273 /* If PWD is accurate, use it instead of calling getcwd. PWD is
249 sometimes a nicer name, and using it may avoid a fatal error if a 274 sometimes a nicer name, and using it may avoid a fatal error if a
250 parent directory is searchable but not readable. */ 275 parent directory is searchable but not readable. */
251 if (pwd 276 if (pwd
252 && (IS_DIRECTORY_SEP (*pwd) || (*pwd && IS_DEVICE_SEP (pwd[1]))) 277 && (IS_DIRECTORY_SEP (*pwd) || (*pwd && IS_DEVICE_SEP (pwd[1])))
278 && (pwdlen = strlen (pwd)) < bufsize_max
253 && stat (pwd, &pwdstat) == 0 279 && stat (pwd, &pwdstat) == 0
254 && stat (".", &dotstat) == 0 280 && stat (".", &dotstat) == 0
255 && dotstat.st_ino == pwdstat.st_ino 281 && dotstat.st_ino == pwdstat.st_ino
256 && dotstat.st_dev == pwdstat.st_dev 282 && dotstat.st_dev == pwdstat.st_dev)
257#ifdef MAXPATHLEN
258 && strlen (pwd) < MAXPATHLEN
259#endif
260 )
261 { 283 {
262 buf = malloc (strlen (pwd) + 1); 284 char *buf = malloc (pwdlen + 1);
263 if (!buf) 285 if (!buf)
264 return NULL; 286 return NULL;
265 strcpy (buf, pwd); 287 return memcpy (buf, pwd, pwdlen + 1);
266 } 288 }
267 else 289 else
268 { 290 {
269 size_t buf_size = 1024; 291 ptrdiff_t buf_size = min (bufsize_max, 1024);
270 buf = malloc (buf_size); 292 char *buf = malloc (buf_size);
271 if (!buf) 293 if (!buf)
272 return NULL; 294 return NULL;
273 for (;;) 295 for (;;)
274 { 296 {
275 if (getcwd (buf, buf_size) == buf) 297 if (getcwd (buf, buf_size) == buf)
276 break; 298 return buf;
277 if (errno != ERANGE) 299 int getcwd_errno = errno;
300 if (getcwd_errno != ERANGE || buf_size == bufsize_max)
278 { 301 {
279 int tmp_errno = errno;
280 free (buf); 302 free (buf);
281 errno = tmp_errno; 303 errno = getcwd_errno;
282 return NULL; 304 return NULL;
283 } 305 }
284 buf_size *= 2; 306 buf_size = buf_size <= bufsize_max / 2 ? 2 * buf_size : bufsize_max;
285 buf = realloc (buf, buf_size); 307 buf = realloc (buf, buf_size);
286 if (!buf) 308 if (!buf)
287 return NULL; 309 return NULL;
288 } 310 }
289 } 311 }
290 return buf;
291} 312}
292 313
293/* Return the current working directory. The result should be freed 314/* Return the current working directory. The result should be freed