From: Lennart Poettering Date: Wed, 25 Feb 2026 10:43:11 +0000 (+0100) Subject: chase: put limit on overall chase cycles X-Git-Tag: v260-rc2~23^2~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=004e60e0fca861db6a924c5e0e7c6cb02ea02fd2;p=thirdparty%2Fsystemd.git chase: put limit on overall chase cycles Let's add some protections in case we deal with inodes owned by an untrusted person, with concurrent access: let's put a limit on how long we traverse, and fail eventually so that live changes cannot send us in circles indefinitely. This reworks the current CHASE_MAX logic so that it not only applies to symlinks transitions, but to any transitions. This also bumps CHASE_MAX a bit, given that it's now bumped on every single iteration of the loop. --- diff --git a/src/basic/chase.c b/src/basic/chase.c index abe85a2a089..11cbcbe3e4d 100644 --- a/src/basic/chase.c +++ b/src/basic/chase.c @@ -128,7 +128,6 @@ static int chaseat_needs_absolute(int dir_fd, const char *path) { int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int *ret_fd) { _cleanup_free_ char *buffer = NULL, *done = NULL; _cleanup_close_ int fd = -EBADF, root_fd = -EBADF; - unsigned max_follow = CHASE_MAX; /* how many symlinks to follow before giving up and returning ELOOP */ bool exists = true, append_trail_slash = false; struct stat st; /* stat obtained from fd */ bool need_absolute = false; /* allocate early to avoid compiler warnings around goto */ @@ -334,12 +333,18 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (FLAGS_SET(flags, CHASE_MUST_BE_DIRECTORY) + FLAGS_SET(flags, CHASE_MUST_BE_REGULAR) + FLAGS_SET(flags, CHASE_MUST_BE_SOCKET) > 1) return -EBADSLT; - for (todo = buffer;;) { + todo = buffer; + for (unsigned n_steps = 0;; n_steps++) { _cleanup_free_ char *first = NULL; _cleanup_close_ int child = -EBADF; struct stat st_child; const char *e; + /* If people change our tree behind our back, they might send us in circles. Put a limit on + * things */ + if (n_steps > CHASE_MAX) + return -ELOOP; + r = path_find_first_component(&todo, /* accept_dot_dot= */ true, &e); if (r < 0) return r; @@ -495,11 +500,6 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int if (FLAGS_SET(flags, CHASE_PROHIBIT_SYMLINKS)) return log_prohibited_symlink(child, flags); - /* This is a symlink, in this case read the destination. But let's make sure we - * don't follow symlinks without bounds. */ - if (--max_follow <= 0) - return -ELOOP; - r = readlinkat_malloc(fd, first, &destination); if (r < 0) return r; diff --git a/src/basic/chase.h b/src/basic/chase.h index d0674aae73c..b770f45a1ec 100644 --- a/src/basic/chase.h +++ b/src/basic/chase.h @@ -35,7 +35,7 @@ typedef enum ChaseFlags { bool unsafe_transition(const struct stat *a, const struct stat *b); /* How many iterations to execute before returning -ELOOP */ -#define CHASE_MAX 32 +#define CHASE_MAX 128U int chase(const char *path_with_prefix, const char *root, ChaseFlags flags, char **ret_path, int *ret_fd);