diff options
| author | Paul Eggert | 2017-10-09 10:30:40 -0700 |
|---|---|---|
| committer | Paul Eggert | 2017-10-09 10:48:55 -0700 |
| commit | 934f08f3de7929e54e9da3271a34bb49a8854f2c (patch) | |
| tree | 99f898f4f6242d35d697749957ca41e813054ca7 /src | |
| parent | 6c2b1e89efe90e1b55bc0f0296e68efa52dbcbc1 (diff) | |
| download | emacs-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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/sysdep.c | 57 |
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) | |||
| 228 | static char * | 228 | static char * |
| 229 | get_current_dir_name_or_unreachable (void) | 229 | get_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 |