4
4
* Copyright (C) 2020 Google LLC.
5
5
*/
6
6
7
+ #include <asm-generic/errno-base.h>
8
+ #include <sys/stat.h>
7
9
#include <test_progs.h>
8
10
#include <linux/limits.h>
9
11
10
12
#include "local_storage.skel.h"
11
13
#include "network_helpers.h"
12
14
13
- int create_and_unlink_file ( void )
15
+ static inline int sys_pidfd_open ( pid_t pid , unsigned int flags )
14
16
{
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
17
31
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
+ }
21
109
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;
25
155
}
26
156
27
157
void test_test_local_storage (void )
28
158
{
159
+ char tmp_exec_path [PATH_MAX ] = "/tmp/copy_of_rmXXXXXX" ;
160
+ int err , serv_sk = -1 , task_fd = -1 ;
29
161
struct local_storage * skel = NULL ;
30
- int err , duration = 0 , serv_sk = -1 ;
31
162
32
163
skel = local_storage__open_and_load ();
33
164
if (CHECK (!skel , "skel_load" , "lsm skeleton failed\n" ))
@@ -37,12 +168,37 @@ void test_test_local_storage(void)
37
168
if (CHECK (err , "attach" , "lsm attach failed: %d\n" , err ))
38
169
goto close_prog ;
39
170
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 ;
41
175
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 ))
44
178
goto close_prog ;
45
179
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
+
46
202
CHECK (skel -> data -> inode_storage_result != 0 , "inode_storage_result" ,
47
203
"inode_local_storage not set\n" );
48
204
@@ -55,6 +211,9 @@ void test_test_local_storage(void)
55
211
56
212
close (serv_sk );
57
213
214
+ close_prog_unlink :
215
+ unlink (tmp_exec_path );
58
216
close_prog :
217
+ close (task_fd );
59
218
local_storage__destroy (skel );
60
219
}
0 commit comments