Fix SCP stack overflow when copying over directories (#565)

This commit is contained in:
Vivian Thiebaut 2022-02-17 20:32:35 -05:00 committed by GitHub
parent c89890c3a4
commit ac7bceff6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

79
scp.c
View File

@ -1262,7 +1262,14 @@ source(int argc, char **argv)
off_t i, statbytes;
size_t amt, nr;
int fd = -1, haderr, indx;
#ifdef WINDOWS
/* PATH_MAX is too large on Windows.*/
/* Allocate memory dynamically for encname and buf to avoid stack overflow on recursive calls.*/
char *last, *name, *buf = NULL, *encname = NULL;
size_t encname_len, buf_len, tmp_len;
#else
char *last, *name, buf[PATH_MAX + 128], encname[PATH_MAX];
#endif
int len;
for (indx = 0; indx < argc; ++indx) {
@ -1274,7 +1281,20 @@ source(int argc, char **argv)
if ((fd = open(name, O_RDONLY|O_NONBLOCK, 0)) == -1)
goto syserr;
if (strchr(name, '\n') != NULL) {
#ifdef WINDOWS
if (!encname) {
encname_len = ((2 * len) < PATH_MAX) ? 2 * len : PATH_MAX;
encname = xmalloc(encname_len);
}
while ((tmp_len = strnvis(encname, name, encname_len, VIS_NL)) >= encname_len) {
if (tmp_len >= PATH_MAX)
break;
encname_len = tmp_len + 1;
encname = xreallocarray(encname, encname_len, sizeof(char));
}
#else
strnvis(encname, name, sizeof(encname), VIS_NL);
#endif
name = encname;
}
if (fstat(fd, &stb) == -1) {
@ -1309,9 +1329,27 @@ syserr: run_err("%s: %s", name, strerror(errno));
goto next;
}
#define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
#ifdef WINDOWS
if (!buf)
{
/*Set the initial size of buf to "strlen(last) + 20" based on multiple tests that*/
/*inidicate that this is usually enough. If not enough, more space will be allocated below.*/
buf_len = ((strlen(last) + 20) < PATH_MAX) ? strlen(last) + 20 : PATH_MAX;
buf = xmalloc(buf_len);
}
while ((tmp_len = snprintf(buf, buf_len, "C%04o %lld %s\n",
(u_int) (stb.st_mode & FILEMODEMASK),
(long long)stb.st_size, last)) >= buf_len) {
if (tmp_len >= PATH_MAX)
break;
buf_len = tmp_len + 1;
buf = xreallocarray(buf, buf_len, sizeof(char));
}
#else
snprintf(buf, sizeof buf, "C%04o %lld %s\n",
(u_int) (stb.st_mode & FILEMODEMASK),
(long long)stb.st_size, last);
#endif
if (verbose_mode)
fmprintf(stderr, "Sending file modes: %s", buf);
(void) atomicio(vwrite, remout, buf, strlen(buf));
@ -1363,6 +1401,12 @@ next: if (fd != -1) {
if (showprogress)
stop_progress_meter();
}
#ifdef WINDOWS
if (encname)
free(encname);
if (buf)
free(buf);
#endif
}
void
@ -1370,7 +1414,16 @@ rsource(char *name, struct stat *statp)
{
DIR *dirp;
struct dirent *dp;
#ifndef WINDOWS
char *last, *vect[1], path[PATH_MAX];
#else
/* PATH_MAX is too large on Windows.*/
/* Allocate memory dynamically for path to avoid stack overflow on recursive calls.*/
char *last, *vect[1], *path;
size_t path_len = 260, len;
path = xmalloc(path_len);
#endif
if (!(dirp = opendir(name))) {
run_err("%s: %s", name, strerror(errno));
@ -1387,8 +1440,19 @@ rsource(char *name, struct stat *statp)
return;
}
}
#ifdef WINDOWS
while ((len = snprintf(path, path_len, "D%04o %d %.1024s\n",
(u_int)(statp->st_mode & FILEMODEMASK), 0, last)) >= path_len)
{
if (len >= PATH_MAX)
break;
path_len = len + 1;
path = xreallocarray(path, path_len, sizeof(char));
}
#else
(void) snprintf(path, sizeof path, "D%04o %d %.1024s\n",
(u_int) (statp->st_mode & FILEMODEMASK), 0, last);
#endif
if (verbose_mode)
fmprintf(stderr, "Entering directory: %s", path);
(void) atomicio(vwrite, remout, path, strlen(path));
@ -1401,14 +1465,29 @@ rsource(char *name, struct stat *statp)
continue;
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
#ifdef WINDOWS
if (strlen(name) + 1 + strlen(dp->d_name) >= PATH_MAX - 1) {
#else
if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) {
#endif
run_err("%s/%s: name too long", name, dp->d_name);
continue;
}
#ifdef WINDOWS
while ((len = snprintf(path, path_len, "%s/%s", name, dp->d_name)) >= path_len) {
path_len = len + 1;
path = xreallocarray(path, path_len, sizeof(char));
}
#else
(void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name);
#endif
vect[0] = path;
source(1, vect);
}
#ifdef WINDOWS
if (path)
free(path);
#endif
(void) closedir(dirp);
(void) atomicio(vwrite, remout, "E\n", 2);
(void) response();