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