History

Merge branch 'ucount-rlimit-fixes-for-v5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace

Pull ucounts fixes from Eric Biederman: "Michal Koutný recently found some bugs in the enforcement of RLIMIT_NPROC in the recent ucount rlimit implementation. In this set of patches I have developed a very conservative approach changing only what is necessary to fix the bugs that I can see clearly. Cleanups and anything that is making the code more consistent can follow after we have the code working as it has historically. The problem is not so much inconsistencies (although those exist) but that it is very difficult to figure out what the code should be doing in the case of RLIMIT_NPROC. All other rlimits are only enforced where the resource is acquired (allocated). RLIMIT_NPROC by necessity needs to be enforced in an additional location, and our current implementation stumbled it's way into that implementation" * 'ucount-rlimit-fixes-for-v5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace: ucounts: Handle wrapping in is_ucounts_overlimit ucounts: Move RLIMIT_NPROC handling after set_user ucounts: Base set_cred_ucounts changes on the real user ucounts: Enforce RLIMIT_NPROC not RLIMIT_NPROC+1 rlimit: Fix RLIMIT_NPROC enforcement failure caused by capability calls in set_user

Linus Torvalds committed 4 years ago

Commit 2d3409e2 parents 4f12b74 0cbae9e4 files · +23 −19Browse files

Unified · Split

4 files
  1. kernel/cred.c +2 −7
  2. kernel/fork.c +5 −5
  3. kernel/sys.c +14 −6
  4. kernel/ucount.c +2 −1
kernel/cred.c+2−7
@@ -665,21 +665,16 @@ EXPORT_SYMBOL(cred_fscmp); · 664 unmodified lines
665 665
666int set_cred_ucounts(struct cred *new)666int set_cred_ucounts(struct cred *new)
667{667{
668 struct task_struct *task = current;
669 const struct cred *old = task->real_cred;
670 struct ucounts *new_ucounts, *old_ucounts = new->ucounts;668 struct ucounts *new_ucounts, *old_ucounts = new->ucounts;
671 669
672 if (new->user == old->user && new->user_ns == old->user_ns)
673 return 0;
674
675 /*670 /*
676 * This optimization is needed because alloc_ucounts() uses locks671 * This optimization is needed because alloc_ucounts() uses locks
677 * for table lookups.672 * for table lookups.
678 */673 */
679 if (old_ucounts->ns == new->user_ns && uid_eq(old_ucounts->uid, new->euid))674 if (old_ucounts->ns == new->user_ns && uid_eq(old_ucounts->uid, new->uid))
680 return 0;675 return 0;
681 676
682 if (!(new_ucounts = alloc_ucounts(new->user_ns, new->euid)))677 if (!(new_ucounts = alloc_ucounts(new->user_ns, new->uid)))
683 return -EAGAIN;678 return -EAGAIN;
684 679
685 new->ucounts = new_ucounts;680 new->ucounts = new_ucounts;
kernel/fork.c+5−5
@@ -2021,18 +2021,18 @@ static __latent_entropy struct task_struct *copy_process( · 2020 unmodified lines
2021#ifdef CONFIG_PROVE_LOCKING2021#ifdef CONFIG_PROVE_LOCKING
2022 DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);2022 DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
2023#endif2023#endif
2024 retval = copy_creds(p, clone_flags);
2025 if (retval < 0)
2026 goto bad_fork_free;
2027
2024 retval = -EAGAIN;2028 retval = -EAGAIN;
2025 if (is_ucounts_overlimit(task_ucounts(p), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC))) {2029 if (is_ucounts_overlimit(task_ucounts(p), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC))) {
2026 if (p->real_cred->user != INIT_USER &&2030 if (p->real_cred->user != INIT_USER &&
2027 !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN))2031 !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN))
2028 goto bad_fork_free;2032 goto bad_fork_cleanup_count;
2029 }2033 }
2030 current->flags &= ~PF_NPROC_EXCEEDED;2034 current->flags &= ~PF_NPROC_EXCEEDED;
2031 2035
2032 retval = copy_creds(p, clone_flags);
2033 if (retval < 0)
2034 goto bad_fork_free;
2035
2036 /*2036 /*
2037 * If multiple threads are within copy_process(), then this check2037 * If multiple threads are within copy_process(), then this check
2038 * triggers too late. This doesn't hurt, the check is only there2038 * triggers too late. This doesn't hurt, the check is only there
kernel/sys.c+14−6
@@ -472,6 +472,16 @@ static int set_user(struct cred *new) · 471 unmodified lines
472 if (!new_user)472 if (!new_user)
473 return -EAGAIN;473 return -EAGAIN;
474 474
475 free_uid(new->user);
476 new->user = new_user;
477 return 0;
478}
479
480static void flag_nproc_exceeded(struct cred *new)
481{
482 if (new->ucounts == current_ucounts())
483 return;
484
475 /*485 /*
476 * We don't fail in case of NPROC limit excess here because too many486 * We don't fail in case of NPROC limit excess here because too many
477 * poorly written programs don't check set*uid() return code, assuming487 * poorly written programs don't check set*uid() return code, assuming
@@ -480,15 +490,10 @@ static int set_user(struct cred *new) · 2 unmodified lines
480 * failure to the execve() stage.490 * failure to the execve() stage.
481 */491 */
482 if (is_ucounts_overlimit(new->ucounts, UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC)) &&492 if (is_ucounts_overlimit(new->ucounts, UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC)) &&
483 new_user != INIT_USER &&493 new->user != INIT_USER)
484 !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN))
485 current->flags |= PF_NPROC_EXCEEDED;494 current->flags |= PF_NPROC_EXCEEDED;
486 else495 else
487 current->flags &= ~PF_NPROC_EXCEEDED;496 current->flags &= ~PF_NPROC_EXCEEDED;
488
489 free_uid(new->user);
490 new->user = new_user;
491 return 0;
492}497}
493 498
494/*499/*
@@ -563,6 +568,7 @@ long __sys_setreuid(uid_t ruid, uid_t euid) · 68 unmodified lines
563 if (retval < 0)568 if (retval < 0)
564 goto error;569 goto error;
565 570
571 flag_nproc_exceeded(new);
566 return commit_creds(new);572 return commit_creds(new);
567 573
568error:574error:
@@ -625,6 +631,7 @@ long __sys_setuid(uid_t uid) · 56 unmodified lines
625 if (retval < 0)631 if (retval < 0)
626 goto error;632 goto error;
627 633
634 flag_nproc_exceeded(new);
628 return commit_creds(new);635 return commit_creds(new);
629 636
630error:637error:
@@ -704,6 +711,7 @@ long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid) · 73 unmodified lines
704 if (retval < 0)711 if (retval < 0)
705 goto error;712 goto error;
706 713
714 flag_nproc_exceeded(new);
707 return commit_creds(new);715 return commit_creds(new);
708 716
709error:717error:
kernel/ucount.c+2−1
@@ -350,7 +350,8 @@ bool is_ucounts_overlimit(struct ucounts *ucounts, enum ucount_type type, unsign · 349 unmodified lines
350 if (rlimit > LONG_MAX)350 if (rlimit > LONG_MAX)
351 max = LONG_MAX;351 max = LONG_MAX;
352 for (iter = ucounts; iter; iter = iter->ns->ucounts) {352 for (iter = ucounts; iter; iter = iter->ns->ucounts) {
353 if (get_ucounts_value(iter, type) > max)353 long val = get_ucounts_value(iter, type);
354 if (val < 0 || val > max)
354 return true;355 return true;
355 max = READ_ONCE(iter->ns->ucount_max[type]);356 max = READ_ONCE(iter->ns->ucount_max[type]);
356 }357 }