When a task is writing to an fd opened by a different task, the perm check should use the credentials of the latter task. Add a test for it.
Unified · Split
| @@ -674,6 +674,73 @@ cleanup: · 673 unmodified lines | ||
| 674 | 674 | return ret; |
| 675 | 675 | } |
| 676 | 676 | |
| 677 | +/* | |
| 678 | + * cgroup migration permission check should be performed based on the | |
| 679 | + * credentials at the time of open instead of write. | |
| 680 | + */ | |
| 681 | +static int test_cgcore_lesser_euid_open(const char *root) | |
| 682 | +{ | |
| 683 | + const uid_t test_euid = 65534; /* usually nobody, any !root is fine */ | |
| 684 | + int ret = KSFT_FAIL; | |
| 685 | + char *cg_test_a = NULL, *cg_test_b = NULL; | |
| 686 | + char *cg_test_a_procs = NULL, *cg_test_b_procs = NULL; | |
| 687 | + int cg_test_b_procs_fd = -1; | |
| 688 | + uid_t saved_uid; | |
| 689 | + | |
| 690 | + cg_test_a = cg_name(root, "cg_test_a"); | |
| 691 | + cg_test_b = cg_name(root, "cg_test_b"); | |
| 692 | + | |
| 693 | + if (!cg_test_a || !cg_test_b) | |
| 694 | + goto cleanup; | |
| 695 | + | |
| 696 | + cg_test_a_procs = cg_name(cg_test_a, "cgroup.procs"); | |
| 697 | + cg_test_b_procs = cg_name(cg_test_b, "cgroup.procs"); | |
| 698 | + | |
| 699 | + if (!cg_test_a_procs || !cg_test_b_procs) | |
| 700 | + goto cleanup; | |
| 701 | + | |
| 702 | + if (cg_create(cg_test_a) || cg_create(cg_test_b)) | |
| 703 | + goto cleanup; | |
| 704 | + | |
| 705 | + if (cg_enter_current(cg_test_a)) | |
| 706 | + goto cleanup; | |
| 707 | + | |
| 708 | + if (chown(cg_test_a_procs, test_euid, -1) || | |
| 709 | + chown(cg_test_b_procs, test_euid, -1)) | |
| 710 | + goto cleanup; | |
| 711 | + | |
| 712 | + saved_uid = geteuid(); | |
| 713 | + if (seteuid(test_euid)) | |
| 714 | + goto cleanup; | |
| 715 | + | |
| 716 | + cg_test_b_procs_fd = open(cg_test_b_procs, O_RDWR); | |
| 717 | + | |
| 718 | + if (seteuid(saved_uid)) | |
| 719 | + goto cleanup; | |
| 720 | + | |
| 721 | + if (cg_test_b_procs_fd < 0) | |
| 722 | + goto cleanup; | |
| 723 | + | |
| 724 | + if (write(cg_test_b_procs_fd, "0", 1) >= 0 || errno != EACCES) | |
| 725 | + goto cleanup; | |
| 726 | + | |
| 727 | + ret = KSFT_PASS; | |
| 728 | + | |
| 729 | +cleanup: | |
| 730 | + cg_enter_current(root); | |
| 731 | + if (cg_test_b_procs_fd >= 0) | |
| 732 | + close(cg_test_b_procs_fd); | |
| 733 | + if (cg_test_b) | |
| 734 | + cg_destroy(cg_test_b); | |
| 735 | + if (cg_test_a) | |
| 736 | + cg_destroy(cg_test_a); | |
| 737 | + free(cg_test_b_procs); | |
| 738 | + free(cg_test_a_procs); | |
| 739 | + free(cg_test_b); | |
| 740 | + free(cg_test_a); | |
| 741 | + return ret; | |
| 742 | +} | |
| 743 | + | |
| 677 | 744 | #define T(x) { x, #x } |
| 678 | 745 | struct corecg_test { |
| 679 | 746 | int (*fn)(const char *root); |
| @@ -689,6 +756,7 @@ struct corecg_test { · 9 unmodified lines | ||
| 689 | 756 | T(test_cgcore_proc_migration), |
| 690 | 757 | T(test_cgcore_thread_migration), |
| 691 | 758 | T(test_cgcore_destroy), |
| 759 | + T(test_cgcore_lesser_euid_open), | |
| 692 | 760 | }; |
| 693 | 761 | #undef T |
| 694 | 762 | |