xref: /linux/tools/testing/selftests/bpf/progs/local_storage.c (revision 6f7e6393d1ce636bb7ec77a7fe7b77458fddf701)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 /*
4  * Copyright 2020 Google LLC.
5  */
6 
7 #include "vmlinux.h"
8 #include <errno.h>
9 #include <bpf/bpf_helpers.h>
10 #include <bpf/bpf_tracing.h>
11 
12 char _license[] SEC("license") = "GPL";
13 
14 #define DUMMY_STORAGE_VALUE 0xdeadbeef
15 
16 __u32 monitored_pid = 0;
17 int inode_storage_result = -1;
18 int sk_storage_result = -1;
19 int task_storage_result = -1;
20 
21 struct local_storage {
22 	struct inode *exec_inode;
23 	__u32 value;
24 };
25 
26 struct {
27 	__uint(type, BPF_MAP_TYPE_INODE_STORAGE);
28 	__uint(map_flags, BPF_F_NO_PREALLOC);
29 	__type(key, int);
30 	__type(value, struct local_storage);
31 } inode_storage_map SEC(".maps");
32 
33 struct {
34 	__uint(type, BPF_MAP_TYPE_SK_STORAGE);
35 	__uint(map_flags, BPF_F_NO_PREALLOC | BPF_F_CLONE);
36 	__type(key, int);
37 	__type(value, struct local_storage);
38 } sk_storage_map SEC(".maps");
39 
40 struct {
41 	__uint(type, BPF_MAP_TYPE_SK_STORAGE);
42 	__uint(map_flags, BPF_F_NO_PREALLOC | BPF_F_CLONE);
43 	__type(key, int);
44 	__type(value, struct local_storage);
45 } sk_storage_map2 SEC(".maps");
46 
47 struct {
48 	__uint(type, BPF_MAP_TYPE_TASK_STORAGE);
49 	__uint(map_flags, BPF_F_NO_PREALLOC);
50 	__type(key, int);
51 	__type(value, struct local_storage);
52 } task_storage_map SEC(".maps");
53 
54 struct {
55 	__uint(type, BPF_MAP_TYPE_TASK_STORAGE);
56 	__uint(map_flags, BPF_F_NO_PREALLOC);
57 	__type(key, int);
58 	__type(value, struct local_storage);
59 } task_storage_map2 SEC(".maps");
60 
61 SEC("lsm/inode_unlink")
62 int BPF_PROG(unlink_hook, struct inode *dir, struct dentry *victim)
63 {
64 	__u32 pid = bpf_get_current_pid_tgid() >> 32;
65 	struct local_storage *storage;
66 	struct task_struct *task;
67 	bool is_self_unlink;
68 
69 	if (pid != monitored_pid)
70 		return 0;
71 
72 	task = bpf_get_current_task_btf();
73 	if (!task)
74 		return 0;
75 
76 	task_storage_result = -1;
77 
78 	storage = bpf_task_storage_get(&task_storage_map, task, 0, 0);
79 	if (!storage)
80 		return 0;
81 
82 	/* Don't let an executable delete itself */
83 	is_self_unlink = storage->exec_inode == victim->d_inode;
84 
85 	storage = bpf_task_storage_get(&task_storage_map2, task, 0,
86 				       BPF_LOCAL_STORAGE_GET_F_CREATE);
87 	if (!storage || storage->value)
88 		return 0;
89 
90 	if (bpf_task_storage_delete(&task_storage_map2, task))
91 		return 0;
92 
93 	if (bpf_task_storage_delete(&task_storage_map, task))
94 		return 0;
95 
96 	task_storage_result = 0;
97 
98 	return is_self_unlink ? -EPERM : 0;
99 }
100 
101 SEC("lsm.s/inode_rename")
102 int BPF_PROG(inode_rename, struct inode *old_dir, struct dentry *old_dentry,
103 	     struct inode *new_dir, struct dentry *new_dentry,
104 	     unsigned int flags)
105 {
106 	struct local_storage *storage;
107 	int err;
108 
109 	/* new_dentry->d_inode can be NULL when the inode is renamed to a file
110 	 * that did not exist before. The helper should be able to handle this
111 	 * NULL pointer.
112 	 */
113 	bpf_inode_storage_get(&inode_storage_map, new_dentry->d_inode, 0,
114 			      BPF_LOCAL_STORAGE_GET_F_CREATE);
115 
116 	storage = bpf_inode_storage_get(&inode_storage_map, old_dentry->d_inode,
117 					0, 0);
118 	if (!storage)
119 		return 0;
120 
121 	if (storage->value != DUMMY_STORAGE_VALUE)
122 		inode_storage_result = -1;
123 
124 	err = bpf_inode_storage_delete(&inode_storage_map, old_dentry->d_inode);
125 	if (!err)
126 		inode_storage_result = err;
127 
128 	return 0;
129 }
130 
131 SEC("lsm.s/socket_bind")
132 int BPF_PROG(socket_bind, struct socket *sock, struct sockaddr *address,
133 	     int addrlen)
134 {
135 	__u32 pid = bpf_get_current_pid_tgid() >> 32;
136 	struct local_storage *storage;
137 	struct sock *sk = sock->sk;
138 
139 	if (pid != monitored_pid || !sk)
140 		return 0;
141 
142 	storage = bpf_sk_storage_get(&sk_storage_map, sk, 0, 0);
143 	if (!storage)
144 		return 0;
145 
146 	sk_storage_result = -1;
147 	if (storage->value != DUMMY_STORAGE_VALUE)
148 		return 0;
149 
150 	/* This tests that we can associate multiple elements
151 	 * with the local storage.
152 	 */
153 	storage = bpf_sk_storage_get(&sk_storage_map2, sk, 0,
154 				     BPF_LOCAL_STORAGE_GET_F_CREATE);
155 	if (!storage)
156 		return 0;
157 
158 	if (bpf_sk_storage_delete(&sk_storage_map2, sk))
159 		return 0;
160 
161 	if (bpf_sk_storage_delete(&sk_storage_map, sk))
162 		return 0;
163 
164 	sk_storage_result = 0;
165 	return 0;
166 }
167 
168 SEC("lsm.s/socket_post_create")
169 int BPF_PROG(socket_post_create, struct socket *sock, int family, int type,
170 	     int protocol, int kern)
171 {
172 	__u32 pid = bpf_get_current_pid_tgid() >> 32;
173 	struct local_storage *storage;
174 	struct sock *sk = sock->sk;
175 
176 	if (pid != monitored_pid || !sk)
177 		return 0;
178 
179 	storage = bpf_sk_storage_get(&sk_storage_map, sk, 0,
180 				     BPF_LOCAL_STORAGE_GET_F_CREATE);
181 	if (!storage)
182 		return 0;
183 
184 	storage->value = DUMMY_STORAGE_VALUE;
185 
186 	return 0;
187 }
188 
189 /* This uses the local storage to remember the inode of the binary that a
190  * process was originally executing.
191  */
192 SEC("lsm.s/bprm_committed_creds")
193 void BPF_PROG(exec, struct linux_binprm *bprm)
194 {
195 	__u32 pid = bpf_get_current_pid_tgid() >> 32;
196 	struct local_storage *storage;
197 
198 	if (pid != monitored_pid)
199 		return;
200 
201 	storage = bpf_task_storage_get(&task_storage_map,
202 				       bpf_get_current_task_btf(), 0,
203 				       BPF_LOCAL_STORAGE_GET_F_CREATE);
204 	if (storage)
205 		storage->exec_inode = bprm->file->f_inode;
206 
207 	storage = bpf_inode_storage_get(&inode_storage_map, bprm->file->f_inode,
208 					0, BPF_LOCAL_STORAGE_GET_F_CREATE);
209 	if (!storage)
210 		return;
211 
212 	storage->value = DUMMY_STORAGE_VALUE;
213 }
214