Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+23 -19
+2 -7
kernel/cred.c
··· 665 665 666 666 int set_cred_ucounts(struct cred *new) 667 667 { 668 - struct task_struct *task = current; 669 - const struct cred *old = task->real_cred; 670 668 struct ucounts *new_ucounts, *old_ucounts = new->ucounts; 671 - 672 - if (new->user == old->user && new->user_ns == old->user_ns) 673 - return 0; 674 669 675 670 /* 676 671 * This optimization is needed because alloc_ucounts() uses locks 677 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 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 678 return -EAGAIN; 684 679 685 680 new->ucounts = new_ucounts;
+5 -5
kernel/fork.c
··· 2021 2021 #ifdef CONFIG_PROVE_LOCKING 2022 2022 DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled); 2023 2023 #endif 2024 + retval = copy_creds(p, clone_flags); 2025 + if (retval < 0) 2026 + goto bad_fork_free; 2027 + 2024 2028 retval = -EAGAIN; 2025 2029 if (is_ucounts_overlimit(task_ucounts(p), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC))) { 2026 2030 if (p->real_cred->user != INIT_USER && 2027 2031 !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) 2028 - goto bad_fork_free; 2032 + goto bad_fork_cleanup_count; 2029 2033 } 2030 2034 current->flags &= ~PF_NPROC_EXCEEDED; 2031 - 2032 - retval = copy_creds(p, clone_flags); 2033 - if (retval < 0) 2034 - goto bad_fork_free; 2035 2035 2036 2036 /* 2037 2037 * If multiple threads are within copy_process(), then this check
+14 -6
kernel/sys.c
··· 472 472 if (!new_user) 473 473 return -EAGAIN; 474 474 475 + free_uid(new->user); 476 + new->user = new_user; 477 + return 0; 478 + } 479 + 480 + static void flag_nproc_exceeded(struct cred *new) 481 + { 482 + if (new->ucounts == current_ucounts()) 483 + return; 484 + 475 485 /* 476 486 * We don't fail in case of NPROC limit excess here because too many 477 487 * poorly written programs don't check set*uid() return code, assuming ··· 490 480 * failure to the execve() stage. 491 481 */ 492 482 if (is_ucounts_overlimit(new->ucounts, UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC)) && 493 - new_user != INIT_USER && 494 - !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) 483 + new->user != INIT_USER) 495 484 current->flags |= PF_NPROC_EXCEEDED; 496 485 else 497 486 current->flags &= ~PF_NPROC_EXCEEDED; 498 - 499 - free_uid(new->user); 500 - new->user = new_user; 501 - return 0; 502 487 } 503 488 504 489 /* ··· 568 563 if (retval < 0) 569 564 goto error; 570 565 566 + flag_nproc_exceeded(new); 571 567 return commit_creds(new); 572 568 573 569 error: ··· 631 625 if (retval < 0) 632 626 goto error; 633 627 628 + flag_nproc_exceeded(new); 634 629 return commit_creds(new); 635 630 636 631 error: ··· 711 704 if (retval < 0) 712 705 goto error; 713 706 707 + flag_nproc_exceeded(new); 714 708 return commit_creds(new); 715 709 716 710 error:
+2 -1
kernel/ucount.c
··· 350 350 if (rlimit > LONG_MAX) 351 351 max = LONG_MAX; 352 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 355 return true; 355 356 max = READ_ONCE(iter->ns->ucount_max[type]); 356 357 }