From 2f865fd3e2cd871387de67394013b1137142357c Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Sat, 30 Jul 2022 00:14:45 -0700 Subject: Handle consecutive slashes better. * safepath.c (safepath_check): At the start of an absolute path and at every component separation, go to the last slash if there are multiple slashes; do not report multiple slashes as an invalid path. Also, if a symbolic link target ends with a slash, then do not add a slash between it and the rest of the path. We thereby conform to the POSIX requirement that if a link target consists of nothing but slashes, then those slashes replace the leading slashes of the rest of the path. --- safepath.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/safepath.c b/safepath.c index f91ff58..270e486 100644 --- a/safepath.c +++ b/safepath.c @@ -298,7 +298,7 @@ int safepath_check(const char *name) struct stat st; int abs = (*name == '/'); const char *start = abs ? "/" : "."; - size_t pos = abs ? 1 : 0; + size_t pos = strspn(name, "/"); /* skip all leading slashes */ char *copy; int ret = SAFEPATH_OK, count = 0, root_checked = abs; @@ -341,10 +341,9 @@ int safepath_check(const char *name) size_t nxslash = pos + strcspn(copy + pos, "/"); int savechar = copy[nxslash]; - /* consecutive slashes */ - if (nxslash == pos) { - ret = SAFEPATH_INVAL; - goto free_out; + if (savechar) { + while (copy[nxslash + 1] == '/') + nxslash++; } /* null terminate the path at the next slash */ @@ -390,7 +389,10 @@ int safepath_check(const char *name) goto free_out; } - if (len == sizeof link) { + if (len == 0) { + ret = SAFEPATH_INVAL; + goto free_out; + } else if (len == sizeof link) { ret = SAFEPATH_TOOLONG; goto free_out; } @@ -430,7 +432,7 @@ int safepath_check(const char *name) ret = SAFEPATH_NOMEM; goto out; } - pos = 1; + pos = strspn(copy, "/"); continue; } else { size_t total = len + 1 + strlen(copy + nxslash + 1) + 1; @@ -440,11 +442,15 @@ int safepath_check(const char *name) goto free_out; } strcpy(resolved, link); - resolved[len] = '/'; - strcpy(resolved + len + 1, copy + nxslash + 1); + if (link[len - 1] != '/') { + resolved[len] = '/'; + strcpy(resolved + len + 1, copy + nxslash + 1); + } else { + strcpy(resolved + len, copy + nxslash + 1); + } free(copy); copy = resolved; - pos = 1; + pos = strspn(copy, "/"); continue; } } else { @@ -469,8 +475,12 @@ int safepath_check(const char *name) } memcpy(resolved, copy, pos); strcpy(resolved + pos, link); - resolved[pos + len] = '/'; - strcpy(resolved + pos + len + 1, copy + nxslash + 1); + if (link[len - 1] != '/') { + resolved[pos + len] = '/'; + strcpy(resolved + pos + len + 1, copy + nxslash + 1); + } else { + strcpy(resolved + pos + len, copy + nxslash + 1); + } free(copy); copy = resolved; continue; -- cgit v1.2.3