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.
This commit is contained in:
Lennart Poettering
2026-02-25 11:43:11 +01:00
parent 97fe03e12f
commit 004e60e0fc
2 changed files with 8 additions and 8 deletions

View File

@@ -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;

View File

@@ -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);