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