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.
This commit is contained in:
parent
6c2b1e89ef
commit
934f08f3de
1 changed files with 39 additions and 18 deletions
57
src/sysdep.c
57
src/sysdep.c
|
@ -228,6 +228,22 @@ init_standard_fds (void)
|
||||||
static char *
|
static char *
|
||||||
get_current_dir_name_or_unreachable (void)
|
get_current_dir_name_or_unreachable (void)
|
||||||
{
|
{
|
||||||
|
/* Use malloc, not xmalloc, since this function can be called before
|
||||||
|
the xmalloc exception machinery is available. */
|
||||||
|
|
||||||
|
char *pwd;
|
||||||
|
|
||||||
|
/* The maximum size of a directory name, including the terminating null.
|
||||||
|
Leave room so that the caller can append a trailing slash. */
|
||||||
|
ptrdiff_t dirsize_max = min (PTRDIFF_MAX, SIZE_MAX) - 1;
|
||||||
|
|
||||||
|
/* The maximum size of a buffer for a file name, including the
|
||||||
|
terminating null. This is bounded by MAXPATHLEN, if available. */
|
||||||
|
ptrdiff_t bufsize_max = dirsize_max;
|
||||||
|
#ifdef MAXPATHLEN
|
||||||
|
bufsize_max = min (bufsize_max, MAXPATHLEN);
|
||||||
|
#endif
|
||||||
|
|
||||||
# if HAVE_GET_CURRENT_DIR_NAME && !BROKEN_GET_CURRENT_DIR_NAME
|
# if HAVE_GET_CURRENT_DIR_NAME && !BROKEN_GET_CURRENT_DIR_NAME
|
||||||
# ifdef HYBRID_MALLOC
|
# ifdef HYBRID_MALLOC
|
||||||
bool use_libc = bss_sbrk_did_unexec;
|
bool use_libc = bss_sbrk_did_unexec;
|
||||||
|
@ -238,56 +254,61 @@ get_current_dir_name_or_unreachable (void)
|
||||||
{
|
{
|
||||||
/* For an unreachable directory, this returns a string that starts
|
/* For an unreachable directory, this returns a string that starts
|
||||||
with "(unreachable)"; see Bug#27871. */
|
with "(unreachable)"; see Bug#27871. */
|
||||||
return get_current_dir_name ();
|
pwd = get_current_dir_name ();
|
||||||
|
if (pwd)
|
||||||
|
{
|
||||||
|
if (strlen (pwd) < dirsize_max)
|
||||||
|
return pwd;
|
||||||
|
free (pwd);
|
||||||
|
errno = ERANGE;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
char *buf;
|
size_t pwdlen;
|
||||||
char *pwd = getenv ("PWD");
|
|
||||||
struct stat dotstat, pwdstat;
|
struct stat dotstat, pwdstat;
|
||||||
|
pwd = getenv ("PWD");
|
||||||
|
|
||||||
/* If PWD is accurate, use it instead of calling getcwd. PWD is
|
/* If PWD is accurate, use it instead of calling getcwd. PWD is
|
||||||
sometimes a nicer name, and using it may avoid a fatal error if a
|
sometimes a nicer name, and using it may avoid a fatal error if a
|
||||||
parent directory is searchable but not readable. */
|
parent directory is searchable but not readable. */
|
||||||
if (pwd
|
if (pwd
|
||||||
&& (IS_DIRECTORY_SEP (*pwd) || (*pwd && IS_DEVICE_SEP (pwd[1])))
|
&& (IS_DIRECTORY_SEP (*pwd) || (*pwd && IS_DEVICE_SEP (pwd[1])))
|
||||||
|
&& (pwdlen = strlen (pwd)) < bufsize_max
|
||||||
&& stat (pwd, &pwdstat) == 0
|
&& stat (pwd, &pwdstat) == 0
|
||||||
&& stat (".", &dotstat) == 0
|
&& stat (".", &dotstat) == 0
|
||||||
&& dotstat.st_ino == pwdstat.st_ino
|
&& dotstat.st_ino == pwdstat.st_ino
|
||||||
&& dotstat.st_dev == pwdstat.st_dev
|
&& dotstat.st_dev == pwdstat.st_dev)
|
||||||
#ifdef MAXPATHLEN
|
|
||||||
&& strlen (pwd) < MAXPATHLEN
|
|
||||||
#endif
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
buf = malloc (strlen (pwd) + 1);
|
char *buf = malloc (pwdlen + 1);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return NULL;
|
return NULL;
|
||||||
strcpy (buf, pwd);
|
return memcpy (buf, pwd, pwdlen + 1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
size_t buf_size = 1024;
|
ptrdiff_t buf_size = min (bufsize_max, 1024);
|
||||||
buf = malloc (buf_size);
|
char *buf = malloc (buf_size);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return NULL;
|
return NULL;
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
if (getcwd (buf, buf_size) == buf)
|
if (getcwd (buf, buf_size) == buf)
|
||||||
break;
|
return buf;
|
||||||
if (errno != ERANGE)
|
int getcwd_errno = errno;
|
||||||
|
if (getcwd_errno != ERANGE || buf_size == bufsize_max)
|
||||||
{
|
{
|
||||||
int tmp_errno = errno;
|
|
||||||
free (buf);
|
free (buf);
|
||||||
errno = tmp_errno;
|
errno = getcwd_errno;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
buf_size *= 2;
|
buf_size = buf_size <= bufsize_max / 2 ? 2 * buf_size : bufsize_max;
|
||||||
buf = realloc (buf, buf_size);
|
buf = realloc (buf, buf_size);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return buf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return the current working directory. The result should be freed
|
/* Return the current working directory. The result should be freed
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue