Skip to content

Commit 59ec715

Browse files
legionusebiederm
authored andcommitted
ucounts: Fix rlimit max values check
The semantics of the rlimit max values differs from ucounts itself. When creating a new userns, we store the current rlimit of the process in ucount_max. Thus, the value of the limit in the parent userns is saved in the created one. The problem is that now we are taking the maximum value for counter from the same userns. So for init_user_ns it will always be RLIM_INFINITY. To fix the problem we need to check the counter value with the max value stored in userns. Reproducer: su - test -c "ulimit -u 3; sleep 5 & sleep 6 & unshare -U --map-root-user sh -c 'sleep 7 & sleep 8 & date; wait'" Before: [1] 175 [2] 176 Fri Nov 26 13:48:20 UTC 2021 [1]- Done sleep 5 [2]+ Done sleep 6 After: [1] 167 [2] 168 sh: fork: retry: Resource temporarily unavailable sh: fork: retry: Resource temporarily unavailable sh: fork: retry: Resource temporarily unavailable sh: fork: retry: Resource temporarily unavailable sh: fork: retry: Resource temporarily unavailable sh: fork: retry: Resource temporarily unavailable sh: fork: retry: Resource temporarily unavailable sh: fork: Interrupted system call [1]- Done sleep 5 [2]+ Done sleep 6 Fixes: c54b245 ("Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace") Reported-by: Gleb Fotengauer-Malinovskiy <glebfm@altlinux.org> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> Signed-off-by: Alexey Gladkov <legion@kernel.org> Link: https://lkml.kernel.org/r/024ec805f6e16896f0b23e094773790d171d2c1c.1638218242.git.legion@kernel.org Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
1 parent 1360572 commit 59ec715

1 file changed

Lines changed: 9 additions & 6 deletions

File tree

kernel/ucount.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -264,15 +264,16 @@ void dec_ucount(struct ucounts *ucounts, enum ucount_type type)
264264
long inc_rlimit_ucounts(struct ucounts *ucounts, enum ucount_type type, long v)
265265
{
266266
struct ucounts *iter;
267+
long max = LONG_MAX;
267268
long ret = 0;
268269

269270
for (iter = ucounts; iter; iter = iter->ns->ucounts) {
270-
long max = READ_ONCE(iter->ns->ucount_max[type]);
271271
long new = atomic_long_add_return(v, &iter->ucount[type]);
272272
if (new < 0 || new > max)
273273
ret = LONG_MAX;
274274
else if (iter == ucounts)
275275
ret = new;
276+
max = READ_ONCE(iter->ns->ucount_max[type]);
276277
}
277278
return ret;
278279
}
@@ -312,15 +313,16 @@ long inc_rlimit_get_ucounts(struct ucounts *ucounts, enum ucount_type type)
312313
{
313314
/* Caller must hold a reference to ucounts */
314315
struct ucounts *iter;
316+
long max = LONG_MAX;
315317
long dec, ret = 0;
316318

317319
for (iter = ucounts; iter; iter = iter->ns->ucounts) {
318-
long max = READ_ONCE(iter->ns->ucount_max[type]);
319320
long new = atomic_long_add_return(1, &iter->ucount[type]);
320321
if (new < 0 || new > max)
321322
goto unwind;
322323
if (iter == ucounts)
323324
ret = new;
325+
max = READ_ONCE(iter->ns->ucount_max[type]);
324326
/*
325327
* Grab an extra ucount reference for the caller when
326328
* the rlimit count was previously 0.
@@ -339,15 +341,16 @@ long inc_rlimit_get_ucounts(struct ucounts *ucounts, enum ucount_type type)
339341
return 0;
340342
}
341343

342-
bool is_ucounts_overlimit(struct ucounts *ucounts, enum ucount_type type, unsigned long max)
344+
bool is_ucounts_overlimit(struct ucounts *ucounts, enum ucount_type type, unsigned long rlimit)
343345
{
344346
struct ucounts *iter;
345-
if (get_ucounts_value(ucounts, type) > max)
346-
return true;
347+
long max = rlimit;
348+
if (rlimit > LONG_MAX)
349+
max = LONG_MAX;
347350
for (iter = ucounts; iter; iter = iter->ns->ucounts) {
348-
max = READ_ONCE(iter->ns->ucount_max[type]);
349351
if (get_ucounts_value(iter, type) > max)
350352
return true;
353+
max = READ_ONCE(iter->ns->ucount_max[type]);
351354
}
352355
return false;
353356
}

0 commit comments

Comments
 (0)