Skip to content

Commit f2e467a

Browse files
thejhbrauner
authored andcommitted
eventpoll: Fix semi-unbounded recursion
Ensure that epoll instances can never form a graph deeper than EP_MAX_NESTS+1 links. Currently, ep_loop_check_proc() ensures that the graph is loop-free and does some recursion depth checks, but those recursion depth checks don't limit the depth of the resulting tree for two reasons: - They don't look upwards in the tree. - If there are multiple downwards paths of different lengths, only one of the paths is actually considered for the depth check since commit 28d82dc ("epoll: limit paths"). Essentially, the current recursion depth check in ep_loop_check_proc() just serves to prevent it from recursing too deeply while checking for loops. A more thorough check is done in reverse_path_check() after the new graph edge has already been created; this checks, among other things, that no paths going upwards from any non-epoll file with a length of more than 5 edges exist. However, this check does not apply to non-epoll files. As a result, it is possible to recurse to a depth of at least roughly 500, tested on v6.15. (I am unsure if deeper recursion is possible; and this may have changed with commit 8c44dac ("eventpoll: Fix priority inversion problem").) To fix it: 1. In ep_loop_check_proc(), note the subtree depth of each visited node, and use subtree depths for the total depth calculation even when a subtree has already been visited. 2. Add ep_get_upwards_depth_proc() for similarly determining the maximum depth of an upwards walk. 3. In ep_loop_check(), use these values to limit the total path length between epoll nodes to EP_MAX_NESTS edges. Fixes: 22bacca ("epoll: prevent creating circular epoll structures") Cc: stable@vger.kernel.org Signed-off-by: Jann Horn <jannh@google.com> Link: https://lore.kernel.org/20250711-epoll-recursion-fix-v1-1-fb2457c33292@google.com Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 3bc4e44 commit f2e467a

1 file changed

Lines changed: 46 additions & 14 deletions

File tree

fs/eventpoll.c

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ struct eventpoll {
218218
/* used to optimize loop detection check */
219219
u64 gen;
220220
struct hlist_head refs;
221+
u8 loop_check_depth;
221222

222223
/*
223224
* usage count, used together with epitem->dying to
@@ -2142,37 +2143,36 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
21422143
}
21432144

21442145
/**
2145-
* ep_loop_check_proc - verify that adding an epoll file inside another
2146-
* epoll structure does not violate the constraints, in
2147-
* terms of closed loops, or too deep chains (which can
2148-
* result in excessive stack usage).
2146+
* ep_loop_check_proc - verify that adding an epoll file @ep inside another
2147+
* epoll file does not create closed loops, and
2148+
* determine the depth of the subtree starting at @ep
21492149
*
21502150
* @ep: the &struct eventpoll to be currently checked.
21512151
* @depth: Current depth of the path being checked.
21522152
*
2153-
* Return: %zero if adding the epoll @file inside current epoll
2154-
* structure @ep does not violate the constraints, or %-1 otherwise.
2153+
* Return: depth of the subtree, or INT_MAX if we found a loop or went too deep.
21552154
*/
21562155
static int ep_loop_check_proc(struct eventpoll *ep, int depth)
21572156
{
2158-
int error = 0;
2157+
int result = 0;
21592158
struct rb_node *rbp;
21602159
struct epitem *epi;
21612160

2161+
if (ep->gen == loop_check_gen)
2162+
return ep->loop_check_depth;
2163+
21622164
mutex_lock_nested(&ep->mtx, depth + 1);
21632165
ep->gen = loop_check_gen;
21642166
for (rbp = rb_first_cached(&ep->rbr); rbp; rbp = rb_next(rbp)) {
21652167
epi = rb_entry(rbp, struct epitem, rbn);
21662168
if (unlikely(is_file_epoll(epi->ffd.file))) {
21672169
struct eventpoll *ep_tovisit;
21682170
ep_tovisit = epi->ffd.file->private_data;
2169-
if (ep_tovisit->gen == loop_check_gen)
2170-
continue;
21712171
if (ep_tovisit == inserting_into || depth > EP_MAX_NESTS)
2172-
error = -1;
2172+
result = INT_MAX;
21732173
else
2174-
error = ep_loop_check_proc(ep_tovisit, depth + 1);
2175-
if (error != 0)
2174+
result = max(result, ep_loop_check_proc(ep_tovisit, depth + 1) + 1);
2175+
if (result > EP_MAX_NESTS)
21762176
break;
21772177
} else {
21782178
/*
@@ -2186,9 +2186,27 @@ static int ep_loop_check_proc(struct eventpoll *ep, int depth)
21862186
list_file(epi->ffd.file);
21872187
}
21882188
}
2189+
ep->loop_check_depth = result;
21892190
mutex_unlock(&ep->mtx);
21902191

2191-
return error;
2192+
return result;
2193+
}
2194+
2195+
/**
2196+
* ep_get_upwards_depth_proc - determine depth of @ep when traversed upwards
2197+
*/
2198+
static int ep_get_upwards_depth_proc(struct eventpoll *ep, int depth)
2199+
{
2200+
int result = 0;
2201+
struct epitem *epi;
2202+
2203+
if (ep->gen == loop_check_gen)
2204+
return ep->loop_check_depth;
2205+
hlist_for_each_entry_rcu(epi, &ep->refs, fllink)
2206+
result = max(result, ep_get_upwards_depth_proc(epi->ep, depth + 1) + 1);
2207+
ep->gen = loop_check_gen;
2208+
ep->loop_check_depth = result;
2209+
return result;
21922210
}
21932211

21942212
/**
@@ -2204,8 +2222,22 @@ static int ep_loop_check_proc(struct eventpoll *ep, int depth)
22042222
*/
22052223
static int ep_loop_check(struct eventpoll *ep, struct eventpoll *to)
22062224
{
2225+
int depth, upwards_depth;
2226+
22072227
inserting_into = ep;
2208-
return ep_loop_check_proc(to, 0);
2228+
/*
2229+
* Check how deep down we can get from @to, and whether it is possible
2230+
* to loop up to @ep.
2231+
*/
2232+
depth = ep_loop_check_proc(to, 0);
2233+
if (depth > EP_MAX_NESTS)
2234+
return -1;
2235+
/* Check how far up we can go from @ep. */
2236+
rcu_read_lock();
2237+
upwards_depth = ep_get_upwards_depth_proc(ep, 0);
2238+
rcu_read_unlock();
2239+
2240+
return (depth+1+upwards_depth > EP_MAX_NESTS) ? -1 : 0;
22092241
}
22102242

22112243
static void clear_tfile_check_list(void)

0 commit comments

Comments
 (0)