44 * Copyright (C) 2020 Google LLC.
55 */
66
7+ #include <asm-generic/errno-base.h>
8+ #include <sys/stat.h>
79#include <test_progs.h>
810#include <linux/limits.h>
911
1012#include "local_storage.skel.h"
1113#include "network_helpers.h"
1214
13- int create_and_unlink_file ( void )
15+ static inline int sys_pidfd_open ( pid_t pid , unsigned int flags )
1416{
15- char fname [PATH_MAX ] = "/tmp/fileXXXXXX" ;
16- int fd ;
17+ return syscall (__NR_pidfd_open , pid , flags );
18+ }
19+
20+ static inline ssize_t copy_file_range (int fd_in , loff_t * off_in , int fd_out ,
21+ loff_t * off_out , size_t len ,
22+ unsigned int flags )
23+ {
24+ return syscall (__NR_copy_file_range , fd_in , off_in , fd_out , off_out ,
25+ len , flags );
26+ }
27+
28+ static unsigned int duration ;
29+
30+ #define TEST_STORAGE_VALUE 0xbeefdead
1731
18- fd = mkstemp (fname );
19- if (fd < 0 )
20- return fd ;
32+ struct storage {
33+ void * inode ;
34+ unsigned int value ;
35+ /* Lock ensures that spin locked versions of local stoage operations
36+ * also work, most operations in this tests are still single threaded
37+ */
38+ struct bpf_spin_lock lock ;
39+ };
40+
41+ /* Copies an rm binary to a temp file. dest is a mkstemp template */
42+ static int copy_rm (char * dest )
43+ {
44+ int fd_in , fd_out = -1 , ret = 0 ;
45+ struct stat stat ;
46+
47+ fd_in = open ("/bin/rm" , O_RDONLY );
48+ if (fd_in < 0 )
49+ return - errno ;
50+
51+ fd_out = mkstemp (dest );
52+ if (fd_out < 0 ) {
53+ ret = - errno ;
54+ goto out ;
55+ }
56+
57+ ret = fstat (fd_in , & stat );
58+ if (ret == -1 ) {
59+ ret = - errno ;
60+ goto out ;
61+ }
62+
63+ ret = copy_file_range (fd_in , NULL , fd_out , NULL , stat .st_size , 0 );
64+ if (ret == -1 ) {
65+ ret = - errno ;
66+ goto out ;
67+ }
68+
69+ /* Set executable permission on the copied file */
70+ ret = chmod (dest , 0100 );
71+ if (ret == -1 )
72+ ret = - errno ;
73+
74+ out :
75+ close (fd_in );
76+ close (fd_out );
77+ return ret ;
78+ }
79+
80+ /* Fork and exec the provided rm binary and return the exit code of the
81+ * forked process and its pid.
82+ */
83+ static int run_self_unlink (int * monitored_pid , const char * rm_path )
84+ {
85+ int child_pid , child_status , ret ;
86+ int null_fd ;
87+
88+ child_pid = fork ();
89+ if (child_pid == 0 ) {
90+ null_fd = open ("/dev/null" , O_WRONLY );
91+ dup2 (null_fd , STDOUT_FILENO );
92+ dup2 (null_fd , STDERR_FILENO );
93+ close (null_fd );
94+
95+ * monitored_pid = getpid ();
96+ /* Use the copied /usr/bin/rm to delete itself
97+ * /tmp/copy_of_rm /tmp/copy_of_rm.
98+ */
99+ ret = execlp (rm_path , rm_path , rm_path , NULL );
100+ if (ret )
101+ exit (errno );
102+ } else if (child_pid > 0 ) {
103+ waitpid (child_pid , & child_status , 0 );
104+ return WEXITSTATUS (child_status );
105+ }
106+
107+ return - EINVAL ;
108+ }
21109
22- close (fd );
23- unlink (fname );
24- return 0 ;
110+ static bool check_syscall_operations (int map_fd , int obj_fd )
111+ {
112+ struct storage val = { .value = TEST_STORAGE_VALUE , .lock = { 0 } },
113+ lookup_val = { .value = 0 , .lock = { 0 } };
114+ int err ;
115+
116+ /* Looking up an existing element should fail initially */
117+ err = bpf_map_lookup_elem_flags (map_fd , & obj_fd , & lookup_val ,
118+ BPF_F_LOCK );
119+ if (CHECK (!err || errno != ENOENT , "bpf_map_lookup_elem" ,
120+ "err:%d errno:%d\n" , err , errno ))
121+ return false;
122+
123+ /* Create a new element */
124+ err = bpf_map_update_elem (map_fd , & obj_fd , & val ,
125+ BPF_NOEXIST | BPF_F_LOCK );
126+ if (CHECK (err < 0 , "bpf_map_update_elem" , "err:%d errno:%d\n" , err ,
127+ errno ))
128+ return false;
129+
130+ /* Lookup the newly created element */
131+ err = bpf_map_lookup_elem_flags (map_fd , & obj_fd , & lookup_val ,
132+ BPF_F_LOCK );
133+ if (CHECK (err < 0 , "bpf_map_lookup_elem" , "err:%d errno:%d" , err ,
134+ errno ))
135+ return false;
136+
137+ /* Check the value of the newly created element */
138+ if (CHECK (lookup_val .value != val .value , "bpf_map_lookup_elem" ,
139+ "value got = %x errno:%d" , lookup_val .value , val .value ))
140+ return false;
141+
142+ err = bpf_map_delete_elem (map_fd , & obj_fd );
143+ if (CHECK (err , "bpf_map_delete_elem()" , "err:%d errno:%d\n" , err ,
144+ errno ))
145+ return false;
146+
147+ /* The lookup should fail, now that the element has been deleted */
148+ err = bpf_map_lookup_elem_flags (map_fd , & obj_fd , & lookup_val ,
149+ BPF_F_LOCK );
150+ if (CHECK (!err || errno != ENOENT , "bpf_map_lookup_elem" ,
151+ "err:%d errno:%d\n" , err , errno ))
152+ return false;
153+
154+ return true;
25155}
26156
27157void test_test_local_storage (void )
28158{
159+ char tmp_exec_path [PATH_MAX ] = "/tmp/copy_of_rmXXXXXX" ;
160+ int err , serv_sk = -1 , task_fd = -1 ;
29161 struct local_storage * skel = NULL ;
30- int err , duration = 0 , serv_sk = -1 ;
31162
32163 skel = local_storage__open_and_load ();
33164 if (CHECK (!skel , "skel_load" , "lsm skeleton failed\n" ))
@@ -37,12 +168,37 @@ void test_test_local_storage(void)
37168 if (CHECK (err , "attach" , "lsm attach failed: %d\n" , err ))
38169 goto close_prog ;
39170
40- skel -> bss -> monitored_pid = getpid ();
171+ task_fd = sys_pidfd_open (getpid (), 0 );
172+ if (CHECK (task_fd < 0 , "pidfd_open" ,
173+ "failed to get pidfd err:%d, errno:%d" , task_fd , errno ))
174+ goto close_prog ;
41175
42- err = create_and_unlink_file ();
43- if ( CHECK ( err < 0 , "exec_cmd" , "err %d errno %d\n" , err , errno ))
176+ if (! check_syscall_operations ( bpf_map__fd ( skel -> maps . task_storage_map ),
177+ task_fd ))
44178 goto close_prog ;
45179
180+ err = copy_rm (tmp_exec_path );
181+ if (CHECK (err < 0 , "copy_rm" , "err %d errno %d\n" , err , errno ))
182+ goto close_prog ;
183+
184+ /* Sets skel->bss->monitored_pid to the pid of the forked child
185+ * forks a child process that executes tmp_exec_path and tries to
186+ * unlink its executable. This operation should be denied by the loaded
187+ * LSM program.
188+ */
189+ err = run_self_unlink (& skel -> bss -> monitored_pid , tmp_exec_path );
190+ if (CHECK (err != EPERM , "run_self_unlink" , "err %d want EPERM\n" , err ))
191+ goto close_prog_unlink ;
192+
193+ /* Set the process being monitored to be the current process */
194+ skel -> bss -> monitored_pid = getpid ();
195+
196+ /* Remove the temporary created executable */
197+ err = unlink (tmp_exec_path );
198+ if (CHECK (err != 0 , "unlink" , "unable to unlink %s: %d" , tmp_exec_path ,
199+ errno ))
200+ goto close_prog_unlink ;
201+
46202 CHECK (skel -> data -> inode_storage_result != 0 , "inode_storage_result" ,
47203 "inode_local_storage not set\n" );
48204
@@ -55,6 +211,9 @@ void test_test_local_storage(void)
55211
56212 close (serv_sk );
57213
214+ close_prog_unlink :
215+ unlink (tmp_exec_path );
58216close_prog :
217+ close (task_fd );
59218 local_storage__destroy (skel );
60219}
0 commit comments