summaryrefslogtreecommitdiff
path: root/glib/gfileutils.c
diff options
context:
space:
mode:
authorPhilip Withnall <pwithnall@endlessos.org>2021-12-02 11:33:09 +0000
committerPhilip Withnall <pwithnall@endlessos.org>2021-12-02 17:50:17 +0000
commitfc25f8d7ef0f4b60bbfaa148e31559006f84e614 (patch)
treeffb91563624132fe7ffe8ed35163cc5506937a2d /glib/gfileutils.c
parent3421a703ca4ca726d4aaf850301417bd57d8a05d (diff)
downloadglib-fc25f8d7ef0f4b60bbfaa148e31559006f84e614.tar.gz
gfileutils: Correctly reset start value when canonicalising paths
If a path starts with more than two slashes, the `start` value was previously incorrect: 1. As per the `g_path_skip_root()` call, `start` was set to point to after the final initial slash. For a path with three initial slashes, this is the character after the third slash. 2. The canonicalisation loop to find the first dir separator sets `output` to point to the character after the first slash (and it overwrites the first slash to be `G_DIR_SEPARATOR`). 3. At this point, with a string `///usr`, `output` points to the second `/`; and `start` points to the `u`. This is incorrect, as `start` should point to the starting character for output, as per the original call to `g_path_skip_root()`. 4. For paths which subsequently include a `..`, this results in the `output > start` check in the `..` loop below not skipping all the characters of a preceding path component, which is then caught by the `G_IS_DIR_SEPARATOR (output[-1])` assertion. Fix this by resetting `start` to `output` after finding the final slash to keep in the output, but before starting the main parsing loop. Relatedly, split `start` into two variables: `after_root` and `output_start`, since the variable actually has two roles in the two parts of the function. Includes a test. This commit is heavily based on suggestions by Sebastian Wilhemi and Sebastian Dröge. Signed-off-by: Philip Withnall <pwithnall@endlessos.org> oss-fuzz#41563
Diffstat (limited to 'glib/gfileutils.c')
-rw-r--r--glib/gfileutils.c19
1 files changed, 10 insertions, 9 deletions
diff --git a/glib/gfileutils.c b/glib/gfileutils.c
index abcf29b25..189b756cc 100644
--- a/glib/gfileutils.c
+++ b/glib/gfileutils.c
@@ -2736,7 +2736,7 @@ gchar *
g_canonicalize_filename (const gchar *filename,
const gchar *relative_to)
{
- gchar *canon, *input, *output, *start;
+ gchar *canon, *input, *output, *after_root, *output_start;
g_return_val_if_fail (relative_to == NULL || g_path_is_absolute (relative_to), NULL);
@@ -2758,9 +2758,9 @@ g_canonicalize_filename (const gchar *filename,
canon = g_strdup (filename);
}
- start = (char *)g_path_skip_root (canon);
+ after_root = (char *)g_path_skip_root (canon);
- if (start == NULL)
+ if (after_root == NULL)
{
/* This shouldn't really happen, as g_get_current_dir() should
return an absolute pathname, but bug 573843 shows this is
@@ -2770,7 +2770,7 @@ g_canonicalize_filename (const gchar *filename,
}
/* Find the first dir separator and use the canonical dir separator. */
- for (output = start - 1;
+ for (output = after_root - 1;
(output >= canon) && G_IS_DIR_SEPARATOR (*output);
output--)
*output = G_DIR_SEPARATOR;
@@ -2783,10 +2783,11 @@ g_canonicalize_filename (const gchar *filename,
* (as does windows too). So, "//" != "/", but more than two slashes
* is treated as "/".
*/
- if (start - output == 1)
+ if (after_root - output == 1)
output++;
- input = start;
+ input = after_root;
+ output_start = output;
while (*input)
{
/* input points to the next non-separator to be processed. */
@@ -2811,13 +2812,13 @@ g_canonicalize_filename (const gchar *filename,
else if (input[0] == '.' && input[1] == '.' &&
(input[2] == 0 || G_IS_DIR_SEPARATOR (input[2])))
{
- if (output > start)
+ if (output > output_start)
{
do
{
output--;
}
- while (!G_IS_DIR_SEPARATOR (output[-1]) && output > start);
+ while (!G_IS_DIR_SEPARATOR (output[-1]) && output > output_start);
}
if (input[2] == 0)
break;
@@ -2837,7 +2838,7 @@ g_canonicalize_filename (const gchar *filename,
}
/* Remove a potentially trailing dir separator */
- if (output > start && G_IS_DIR_SEPARATOR (output[-1]))
+ if (output > output_start && G_IS_DIR_SEPARATOR (output[-1]))
output--;
*output = '\0';