11/* SPDX-License-Identifier: GPL-2.0 */
22
3+ #define _GNU_SOURCE
34#include <linux/limits.h>
5+ #include <linux/sched.h>
46#include <sys/types.h>
57#include <sys/mman.h>
68#include <sys/wait.h>
79#include <unistd.h>
810#include <fcntl.h>
11+ #include <sched.h>
912#include <stdio.h>
1013#include <errno.h>
1114#include <signal.h>
@@ -741,6 +744,99 @@ static int test_cgcore_lesser_euid_open(const char *root)
741744 return ret ;
742745}
743746
747+ struct lesser_ns_open_thread_arg {
748+ const char * path ;
749+ int fd ;
750+ int err ;
751+ };
752+
753+ static int lesser_ns_open_thread_fn (void * arg )
754+ {
755+ struct lesser_ns_open_thread_arg * targ = arg ;
756+
757+ targ -> fd = open (targ -> path , O_RDWR );
758+ targ -> err = errno ;
759+ return 0 ;
760+ }
761+
762+ /*
763+ * cgroup migration permission check should be performed based on the cgroup
764+ * namespace at the time of open instead of write.
765+ */
766+ static int test_cgcore_lesser_ns_open (const char * root )
767+ {
768+ static char stack [65536 ];
769+ const uid_t test_euid = 65534 ; /* usually nobody, any !root is fine */
770+ int ret = KSFT_FAIL ;
771+ char * cg_test_a = NULL , * cg_test_b = NULL ;
772+ char * cg_test_a_procs = NULL , * cg_test_b_procs = NULL ;
773+ int cg_test_b_procs_fd = -1 ;
774+ struct lesser_ns_open_thread_arg targ = { .fd = -1 };
775+ pid_t pid ;
776+ int status ;
777+
778+ cg_test_a = cg_name (root , "cg_test_a" );
779+ cg_test_b = cg_name (root , "cg_test_b" );
780+
781+ if (!cg_test_a || !cg_test_b )
782+ goto cleanup ;
783+
784+ cg_test_a_procs = cg_name (cg_test_a , "cgroup.procs" );
785+ cg_test_b_procs = cg_name (cg_test_b , "cgroup.procs" );
786+
787+ if (!cg_test_a_procs || !cg_test_b_procs )
788+ goto cleanup ;
789+
790+ if (cg_create (cg_test_a ) || cg_create (cg_test_b ))
791+ goto cleanup ;
792+
793+ if (cg_enter_current (cg_test_b ))
794+ goto cleanup ;
795+
796+ if (chown (cg_test_a_procs , test_euid , -1 ) ||
797+ chown (cg_test_b_procs , test_euid , -1 ))
798+ goto cleanup ;
799+
800+ targ .path = cg_test_b_procs ;
801+ pid = clone (lesser_ns_open_thread_fn , stack + sizeof (stack ),
802+ CLONE_NEWCGROUP | CLONE_FILES | CLONE_VM | SIGCHLD ,
803+ & targ );
804+ if (pid < 0 )
805+ goto cleanup ;
806+
807+ if (waitpid (pid , & status , 0 ) < 0 )
808+ goto cleanup ;
809+
810+ if (!WIFEXITED (status ))
811+ goto cleanup ;
812+
813+ cg_test_b_procs_fd = targ .fd ;
814+ if (cg_test_b_procs_fd < 0 )
815+ goto cleanup ;
816+
817+ if (cg_enter_current (cg_test_a ))
818+ goto cleanup ;
819+
820+ if ((status = write (cg_test_b_procs_fd , "0" , 1 )) >= 0 || errno != ENOENT )
821+ goto cleanup ;
822+
823+ ret = KSFT_PASS ;
824+
825+ cleanup :
826+ cg_enter_current (root );
827+ if (cg_test_b_procs_fd >= 0 )
828+ close (cg_test_b_procs_fd );
829+ if (cg_test_b )
830+ cg_destroy (cg_test_b );
831+ if (cg_test_a )
832+ cg_destroy (cg_test_a );
833+ free (cg_test_b_procs );
834+ free (cg_test_a_procs );
835+ free (cg_test_b );
836+ free (cg_test_a );
837+ return ret ;
838+ }
839+
744840#define T (x ) { x, #x }
745841struct corecg_test {
746842 int (* fn )(const char * root );
@@ -757,6 +853,7 @@ struct corecg_test {
757853 T (test_cgcore_thread_migration ),
758854 T (test_cgcore_destroy ),
759855 T (test_cgcore_lesser_euid_open ),
856+ T (test_cgcore_lesser_ns_open ),
760857};
761858#undef T
762859
0 commit comments