xref: /linux/kernel/bpf/bpf_insn_array.c (revision fba62d33c1254ee706c724e2b3b72fc6e237b7ad)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2025 Isovalent */
3 
4 #include <linux/bpf.h>
5 
6 struct bpf_insn_array {
7 	struct bpf_map map;
8 	atomic_t used;
9 	long *ips;
10 	DECLARE_FLEX_ARRAY(struct bpf_insn_array_value, values);
11 };
12 
13 #define cast_insn_array(MAP_PTR) \
14 	container_of((MAP_PTR), struct bpf_insn_array, map)
15 
16 #define INSN_DELETED ((u32)-1)
17 
18 static inline u64 insn_array_alloc_size(u32 max_entries)
19 {
20 	const u64 base_size = sizeof(struct bpf_insn_array);
21 	const u64 entry_size = sizeof(struct bpf_insn_array_value);
22 
23 	return base_size + max_entries * (entry_size + sizeof(long));
24 }
25 
26 static int insn_array_alloc_check(union bpf_attr *attr)
27 {
28 	u32 value_size = sizeof(struct bpf_insn_array_value);
29 
30 	if (attr->max_entries == 0 || attr->key_size != 4 ||
31 	    attr->value_size != value_size || attr->map_flags != 0)
32 		return -EINVAL;
33 
34 	return 0;
35 }
36 
37 static void insn_array_free(struct bpf_map *map)
38 {
39 	struct bpf_insn_array *insn_array = cast_insn_array(map);
40 
41 	bpf_map_area_free(insn_array);
42 }
43 
44 static struct bpf_map *insn_array_alloc(union bpf_attr *attr)
45 {
46 	u64 size = insn_array_alloc_size(attr->max_entries);
47 	struct bpf_insn_array *insn_array;
48 
49 	insn_array = bpf_map_area_alloc(size, NUMA_NO_NODE);
50 	if (!insn_array)
51 		return ERR_PTR(-ENOMEM);
52 
53 	/* ips are allocated right after the insn_array->values[] array */
54 	insn_array->ips = (void *)&insn_array->values[attr->max_entries];
55 
56 	bpf_map_init_from_attr(&insn_array->map, attr);
57 
58 	/* BPF programs aren't allowed to write to the map */
59 	insn_array->map.map_flags |= BPF_F_RDONLY_PROG;
60 
61 	return &insn_array->map;
62 }
63 
64 static void *insn_array_lookup_elem(struct bpf_map *map, void *key)
65 {
66 	struct bpf_insn_array *insn_array = cast_insn_array(map);
67 	u32 index = *(u32 *)key;
68 
69 	if (unlikely(index >= insn_array->map.max_entries))
70 		return NULL;
71 
72 	return &insn_array->values[index];
73 }
74 
75 static long insn_array_update_elem(struct bpf_map *map, void *key, void *value, u64 map_flags)
76 {
77 	struct bpf_insn_array *insn_array = cast_insn_array(map);
78 	u32 index = *(u32 *)key;
79 	struct bpf_insn_array_value val = {};
80 
81 	if (unlikely(index >= insn_array->map.max_entries))
82 		return -E2BIG;
83 
84 	if (unlikely(map_flags & BPF_NOEXIST))
85 		return -EEXIST;
86 
87 	copy_map_value(map, &val, value);
88 	if (val.jitted_off || val.xlated_off)
89 		return -EINVAL;
90 
91 	insn_array->values[index].orig_off = val.orig_off;
92 
93 	return 0;
94 }
95 
96 static long insn_array_delete_elem(struct bpf_map *map, void *key)
97 {
98 	return -EINVAL;
99 }
100 
101 static int insn_array_check_btf(struct bpf_map *map,
102 			      const struct btf *btf,
103 			      const struct btf_type *key_type,
104 			      const struct btf_type *value_type)
105 {
106 	if (!btf_type_is_i32(key_type))
107 		return -EINVAL;
108 
109 	if (!btf_type_is_i64(value_type))
110 		return -EINVAL;
111 
112 	return 0;
113 }
114 
115 static u64 insn_array_mem_usage(const struct bpf_map *map)
116 {
117 	return insn_array_alloc_size(map->max_entries);
118 }
119 
120 static int insn_array_map_direct_value_addr(const struct bpf_map *map, u64 *imm, u32 off)
121 {
122 	struct bpf_insn_array *insn_array = cast_insn_array(map);
123 
124 	if ((off % sizeof(long)) != 0 ||
125 	    (off / sizeof(long)) >= map->max_entries)
126 		return -EACCES;
127 
128 	/* from BPF's point of view, this map is a jump table */
129 	*imm = (unsigned long)insn_array->ips;
130 
131 	return 0;
132 }
133 
134 BTF_ID_LIST_SINGLE(insn_array_btf_ids, struct, bpf_insn_array)
135 
136 const struct bpf_map_ops insn_array_map_ops = {
137 	.map_alloc_check = insn_array_alloc_check,
138 	.map_alloc = insn_array_alloc,
139 	.map_free = insn_array_free,
140 	.map_get_next_key = bpf_array_get_next_key,
141 	.map_lookup_elem = insn_array_lookup_elem,
142 	.map_update_elem = insn_array_update_elem,
143 	.map_delete_elem = insn_array_delete_elem,
144 	.map_check_btf = insn_array_check_btf,
145 	.map_mem_usage = insn_array_mem_usage,
146 	.map_direct_value_addr = insn_array_map_direct_value_addr,
147 	.map_btf_id = &insn_array_btf_ids[0],
148 };
149 
150 static inline bool is_frozen(struct bpf_map *map)
151 {
152 	guard(mutex)(&map->freeze_mutex);
153 
154 	return map->frozen;
155 }
156 
157 static bool is_insn_array(const struct bpf_map *map)
158 {
159 	return map->map_type == BPF_MAP_TYPE_INSN_ARRAY;
160 }
161 
162 static inline bool valid_offsets(const struct bpf_insn_array *insn_array,
163 				 const struct bpf_prog *prog)
164 {
165 	u32 off;
166 	int i;
167 
168 	for (i = 0; i < insn_array->map.max_entries; i++) {
169 		off = insn_array->values[i].orig_off;
170 
171 		if (off >= prog->len)
172 			return false;
173 
174 		if (off > 0) {
175 			if (prog->insnsi[off-1].code == (BPF_LD | BPF_DW | BPF_IMM))
176 				return false;
177 		}
178 	}
179 
180 	return true;
181 }
182 
183 int bpf_insn_array_init(struct bpf_map *map, const struct bpf_prog *prog)
184 {
185 	struct bpf_insn_array *insn_array = cast_insn_array(map);
186 	struct bpf_insn_array_value *values = insn_array->values;
187 	int i;
188 
189 	if (!is_frozen(map))
190 		return -EINVAL;
191 
192 	if (!valid_offsets(insn_array, prog))
193 		return -EINVAL;
194 
195 	/*
196 	 * There can be only one program using the map
197 	 */
198 	if (atomic_xchg(&insn_array->used, 1))
199 		return -EBUSY;
200 
201 	/*
202 	 * Reset the map to its pre-verification state. The xlated and jitted
203 	 * offsets and the jitted target pointers are recomputed by the verifier
204 	 * and the JIT for this program, so any values left by a previous owner
205 	 * must be cleared here.
206 	 */
207 	for (i = 0; i < map->max_entries; i++) {
208 		values[i].xlated_off = values[i].orig_off;
209 		values[i].jitted_off = 0;
210 		insn_array->ips[i] = 0;
211 	}
212 
213 	return 0;
214 }
215 
216 int bpf_insn_array_ready(struct bpf_map *map)
217 {
218 	struct bpf_insn_array *insn_array = cast_insn_array(map);
219 	int i;
220 
221 	for (i = 0; i < map->max_entries; i++) {
222 		if (insn_array->values[i].xlated_off == INSN_DELETED)
223 			continue;
224 		if (!insn_array->ips[i])
225 			return -EFAULT;
226 	}
227 
228 	return 0;
229 }
230 
231 void bpf_insn_array_release(struct bpf_map *map)
232 {
233 	struct bpf_insn_array *insn_array = cast_insn_array(map);
234 
235 	/* Paired with atomic_xchg() in bpf_insn_array_init(). */
236 	atomic_set_release(&insn_array->used, 0);
237 }
238 
239 void bpf_insn_array_adjust(struct bpf_map *map, u32 first, u32 len)
240 {
241 	struct bpf_insn_array *insn_array = cast_insn_array(map);
242 	int i;
243 
244 	if (len <= 1)
245 		return;
246 
247 	for (i = 0; i < map->max_entries; i++) {
248 		if (insn_array->values[i].xlated_off < first)
249 			continue;
250 		if (insn_array->values[i].xlated_off == INSN_DELETED)
251 			continue;
252 		insn_array->values[i].xlated_off += len - 1;
253 	}
254 }
255 
256 void bpf_insn_array_adjust_after_remove(struct bpf_map *map, u32 off, u32 len)
257 {
258 	struct bpf_insn_array *insn_array = cast_insn_array(map);
259 	int i;
260 
261 	for (i = 0; i < map->max_entries; i++) {
262 		if (insn_array->values[i].xlated_off < off)
263 			continue;
264 		if (insn_array->values[i].xlated_off == INSN_DELETED)
265 			continue;
266 		if (insn_array->values[i].xlated_off < off + len)
267 			insn_array->values[i].xlated_off = INSN_DELETED;
268 		else
269 			insn_array->values[i].xlated_off -= len;
270 	}
271 }
272 
273 /*
274  * This function is called by JITs. The image is the real program
275  * image, the offsets array set up the xlated -> jitted mapping.
276  * The offsets[xlated] offset should point to the beginning of
277  * the jitted instruction.
278  */
279 void bpf_prog_update_insn_ptrs(struct bpf_prog *prog, u32 *offsets, void *image)
280 {
281 	struct bpf_insn_array *insn_array;
282 	struct bpf_map *map;
283 	u32 xlated_off;
284 	int i, j;
285 
286 	if (!offsets || !image)
287 		return;
288 
289 	for (i = 0; i < prog->aux->used_map_cnt; i++) {
290 		map = prog->aux->used_maps[i];
291 		if (!is_insn_array(map))
292 			continue;
293 
294 		insn_array = cast_insn_array(map);
295 		for (j = 0; j < map->max_entries; j++) {
296 			xlated_off = insn_array->values[j].xlated_off;
297 			if (xlated_off == INSN_DELETED)
298 				continue;
299 			if (xlated_off < prog->aux->subprog_start)
300 				continue;
301 			xlated_off -= prog->aux->subprog_start;
302 			if (xlated_off >= prog->len)
303 				continue;
304 
305 			insn_array->values[j].jitted_off = offsets[xlated_off];
306 			insn_array->ips[j] = (long)(image + offsets[xlated_off]);
307 		}
308 	}
309 }
310