diff options
author | Andreas Gruenbacher <agruen@gnu.org> | 2015-02-28 06:12:34 +0100 |
---|---|---|
committer | Andreas Gruenbacher <agruen@gnu.org> | 2015-03-05 22:57:07 +0100 |
commit | a025a51ca5c173c958ad446d9ba5718019867ba8 (patch) | |
tree | fe8a833b06f4e0992b4336a6452cc249d02c5b9e | |
parent | ef609c26b22e5d6ea3c891e4c87ab1c679146f5f (diff) | |
download | patch-a025a51ca5c173c958ad446d9ba5718019867ba8.tar.gz |
Limit the number of path components
src/safe.c (MAX_PATH_COMPONENTS): The maximum number of path components
allowed.
(count_path_components): New function.
(traverse_another_path): Fail if the number of path components gets too high.
-rw-r--r-- | src/safe.c | 39 |
1 files changed, 36 insertions, 3 deletions
@@ -45,6 +45,8 @@ # define EFTYPE 0 #endif +static const unsigned int MAX_PATH_COMPONENTS = 1024; + /* Path lookup results are cached in a hash table + LRU list. When the cache is full, the oldest entries are removed. */ @@ -239,6 +241,24 @@ out: return entry; } +static unsigned int count_path_components (const char *path) +{ + unsigned int components; + + while (ISSLASH (*path)) + path++; + if (! *path) + return 1; + for (components = 0; *path; components++) + { + while (*path && ! ISSLASH (*path)) + path++; + while (ISSLASH (*path)) + path++; + } + return components; +} + /* A symlink to resolve. */ struct symlink { struct symlink *prev; @@ -283,8 +303,6 @@ static struct symlink *read_symlink(int dirfd, const char *name) errno = EXDEV; goto fail; } - /* FIXME: Limit the depth of recursion and the number of symlinks - * that will be followed. */ return symlink; fail: @@ -362,6 +380,13 @@ static int traverse_another_path (const char **pathname, int keepfd) const char *path = *pathname, *last; struct cached_dirfd *dir = &cwd; struct symlink *stack = NULL; + unsigned int steps = count_path_components (path); + + if (steps > MAX_PATH_COMPONENTS) + { + errno = ELOOP; + return -1; + } if (! *path || IS_ABSOLUTE_FILE_NAME (path)) return AT_FDCWD; @@ -409,7 +434,15 @@ static int traverse_another_path (const char **pathname, int keepfd) if (stack && ! *stack->path) pop_symlink (&stack); if (symlink) - push_symlink (&stack, symlink); + { + push_symlink (&stack, symlink); + steps += count_path_components (symlink->path); + if (steps > MAX_PATH_COMPONENTS) + { + errno = ELOOP; + goto fail; + } + } } *pathname = last; if (debug & 32) |