summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Gruenbacher <agruen@gnu.org>2015-02-28 06:12:34 +0100
committerAndreas Gruenbacher <agruen@gnu.org>2015-03-05 22:57:07 +0100
commita025a51ca5c173c958ad446d9ba5718019867ba8 (patch)
treefe8a833b06f4e0992b4336a6452cc249d02c5b9e
parentef609c26b22e5d6ea3c891e4c87ab1c679146f5f (diff)
downloadpatch-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.c39
1 files changed, 36 insertions, 3 deletions
diff --git a/src/safe.c b/src/safe.c
index 79ff2d2..d4a2a4b 100644
--- a/src/safe.c
+++ b/src/safe.c
@@ -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)