// SPDX-License-Identifier: GPL-2.0-only/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */#include <linux/bpf.h>#include <linux/btf.h>#include <linux/bpf_verifier.h>#include <linux/filter.h>#include <linux/vmalloc.h>#include <linux/bsearch.h>#include <linux/sort.h>#include <linux/perf_event.h>#include <net/xdp.h>#include "disasm.h" #define verbose(env, fmt, args...) bpf_verifier_log_write(env, fmt, ##args) /* * Matches BPF_PROBE_ATOMIC too: bpf_convert_ctx_accesses() rewrites arena * atomics before bpf_opt_subreg_zext_lo32_rnd_hi32() runs. */static bool is_cmpxchg_insn(const struct bpf_insn *insn){ return BPF_CLASS(insn->code) == BPF_STX && (BPF_MODE(insn->code) == BPF_ATOMIC || BPF_MODE(insn->code) == BPF_PROBE_ATOMIC) && insn->imm == BPF_CMPXCHG;} /* Returns true if 'insn' is an address space cast instruction translated as BPF_ALU op */static bool is_addr_space_cast32(struct bpf_prog *prog, const struct bpf_insn *insn){ struct bpf_map *arena = (struct bpf_map *)prog->aux->arena; if (insn->code != (BPF_ALU64 | BPF_MOV | BPF_X) || insn->off != BPF_ADDR_SPACE_CAST) return false; /* cast from as(1) to as(0) */ if (insn->imm == 1) return true; /* cast from as(0) to as(1) */ if (insn->imm == 1 << 16) return arena && arena->map_flags & BPF_F_NO_USER_CONV; /* non-BPF_F_NO_USER_CONV cast from as(0) to as(1) should be handled by JIT */ return false;} /* Return the regno defined by the insn, or -1. */static int insn_def_regno(const struct bpf_insn *insn){ switch (BPF_CLASS(insn->code)) { case BPF_JMP: case BPF_JMP32: case BPF_ST: return -1; case BPF_STX: return bpf_atomic_load_reg(insn); default: return insn->dst_reg; }} /* * For use only in combination with insn_def_regno() >= 0. * Returns TRUE if the destination register operates on 64-bit, * otherwise return FALSE. */static bool bpf_is_reg64(struct bpf_prog *prog, struct bpf_insn *insn){ u8 class = BPF_CLASS(insn->code); u8 mode = BPF_MODE(insn->code); u8 size = BPF_SIZE(insn->code); u8 op = BPF_OP(insn->code); bool mode_mem; /* subregister endiness swap */ if ((class == BPF_ALU || class == BPF_ALU64) && op == BPF_END && insn->imm != 64) return false; /* w0 += 1 */ if (class == BPF_ALU && op != BPF_END) return false; /* address space casts converted to BPF_ALU, see bpf_do_misc_fixups() */ if (is_addr_space_cast32(prog, insn)) return false; /* non 64-bit, non signed extended loads */ mode_mem = mode == BPF_MEM || mode == BPF_PROBE_MEM || mode == BPF_PROBE_MEM32; if (class == BPF_LDX && mode_mem && size != BPF_DW) return false; /* atomics, see insn_def_regno() */ if (class == BPF_STX && size != BPF_DW) return false; /* both LD_IND and LD_ABS return 32-bit data. */ if (class == BPF_LD && (mode == BPF_IND || mode == BPF_ABS)) return false; /* Conservatively return true at default. */ return true;} /* * Return the 32-bit subregister defined by INSN, or -1 if INSN does not * explicitly define a 32-bit value. */int bpf_insn_def32(struct bpf_prog *prog, struct bpf_insn *insn){ int dst_reg = insn_def_regno(insn); if (dst_reg < 0 || bpf_is_reg64(prog, insn)) return -1; return dst_reg;} static int kfunc_desc_cmp_by_imm_off(const void *a, const void *b){ const struct bpf_kfunc_desc *d0 = a; const struct bpf_kfunc_desc *d1 = b; if (d0->imm != d1->imm) return d0->imm < d1->imm ? -1 : 1; if (d0->offset != d1->offset) return d0->offset < d1->offset ? -1 : 1; return 0;} const struct btf_func_model *bpf_jit_find_kfunc_model(const struct bpf_prog *prog, const struct bpf_insn *insn){ const struct bpf_kfunc_desc desc = { .imm = insn->imm, .offset = insn->off, }; const struct bpf_kfunc_desc *res; struct bpf_kfunc_desc_tab *tab; tab = prog->aux->kfunc_tab; res = bsearch(&desc, tab->descs, tab->nr_descs, sizeof(tab->descs[0]), kfunc_desc_cmp_by_imm_off); return res ? &res->func_model : NULL;} static int set_kfunc_desc_imm(struct bpf_verifier_env *env, struct bpf_kfunc_desc *desc){ unsigned long call_imm; if (bpf_jit_supports_far_kfunc_call()) { call_imm = desc->func_id; } else { call_imm = BPF_CALL_IMM(desc->addr); /* Check whether the relative offset overflows desc->imm */ if ((unsigned long)(s32)call_imm != call_imm) { verbose(env, "address of kernel func_id %u is out of range\n", desc->func_id); return -EINVAL; } } desc->imm = call_imm; return 0;} static int sort_kfunc_descs_by_imm_off(struct bpf_verifier_env *env){ struct bpf_kfunc_desc_tab *tab; int i, err; tab = env->prog->aux->kfunc_tab; if (!tab) return 0; for (i = 0; i < tab->nr_descs; i++) { err = set_kfunc_desc_imm(env, &tab->descs[i]); if (err) return err; } sort(tab->descs, tab->nr_descs, sizeof(tab->descs[0]), kfunc_desc_cmp_by_imm_off, NULL); return 0;} static int add_kfunc_in_insns(struct bpf_verifier_env *env, struct bpf_insn *insn, int cnt){ int i, ret; for (i = 0; i < cnt; i++, insn++) { if (bpf_pseudo_kfunc_call(insn)) { ret = bpf_add_kfunc_call(env, insn->imm, insn->off); if (ret < 0) return ret; } } return 0;} #ifndef CONFIG_BPF_JIT_ALWAYS_ONstatic int get_callee_stack_depth(struct bpf_verifier_env *env, const struct bpf_insn *insn, int idx){ int start = idx + insn->imm + 1, subprog; subprog = bpf_find_subprog(env, start); if (verifier_bug_if(subprog < 0, env, "get stack depth: no program at insn %d", start)) return -EFAULT; return env->subprog_info[subprog].stack_depth;}#endif /* single env->prog->insni[off] instruction was replaced with the range * insni[off, off + cnt). Adjust corresponding insn_aux_data by copying * [0, off) and [off, end) to new locations, so the patched range stays zero */static void adjust_insn_aux_data(struct bpf_verifier_env *env, struct bpf_prog *new_prog, u32 off, u32 cnt){ struct bpf_insn_aux_data *data = env->insn_aux_data; struct bpf_insn *insn = new_prog->insnsi; u32 old_seen = data[off].seen; u32 prog_len; int i; /* aux info at OFF always needs adjustment, no matter fast path * (cnt == 1) is taken or not. There is no guarantee INSN at OFF is the * original insn at old prog. */ data[off].zext_dst = bpf_insn_def32(new_prog, insn + off + cnt - 1) >= 0; if (cnt == 1) return; prog_len = new_prog->len; env->insn_aux_data_len = prog_len; memmove(data + off + cnt - 1, data + off, sizeof(struct bpf_insn_aux_data) * (prog_len - off - cnt + 1)); memset(data + off, 0, sizeof(struct bpf_insn_aux_data) * (cnt - 1)); for (i = off; i < off + cnt - 1; i++) { /* Expand insni[off]'s seen count to the patched range. */ data[i].seen = old_seen; data[i].zext_dst = bpf_insn_def32(new_prog, insn + i) >= 0; } /* * The indirect_target flag of the original instruction was moved to the last of the * new instructions by the above memmove and memset, but the indirect jump target is * actually the first instruction, so move it back. This also matches with the behavior * of bpf_insn_array_adjust(), which preserves xlated_off to point to the first new * instruction. */ if (data[off + cnt - 1].indirect_target) { data[off].indirect_target = 1; data[off + cnt - 1].indirect_target = 0; }} static void adjust_subprog_starts(struct bpf_verifier_env *env, u32 off, u32 len){ int i; if (len == 1) return; /* NOTE: fake 'exit' subprog should be updated as well. */ for (i = 0; i <= env->subprog_cnt; i++) { if (env->subprog_info[i].start <= off) continue; env->subprog_info[i].start += len - 1; }} static void adjust_insn_arrays(struct bpf_verifier_env *env, u32 off, u32 len){ int i; if (len == 1) return; for (i = 0; i < env->insn_array_map_cnt; i++) bpf_insn_array_adjust(env->insn_array_maps[i], off, len);} static void adjust_insn_arrays_after_remove(struct bpf_verifier_env *env, u32 off, u32 len){ int i; for (i = 0; i < env->insn_array_map_cnt; i++) bpf_insn_array_adjust_after_remove(env->insn_array_maps[i], off, len);} static void adjust_poke_descs(struct bpf_prog *prog, u32 off, u32 len){ struct bpf_jit_poke_descriptor *tab = prog->aux->poke_tab; int i, sz = prog->aux->size_poke_tab; struct bpf_jit_poke_descriptor *desc; for (i = 0; i < sz; i++) { desc = &tab[i]; if (desc->insn_idx <= off) continue; desc->insn_idx += len - 1; }} struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off, const struct bpf_insn *patch, u32 len){ struct bpf_prog *new_prog; struct bpf_insn_aux_data *new_data = NULL; if (len > 1) { new_data = vrealloc(env->insn_aux_data, array_size(env->prog->len + len - 1, sizeof(struct bpf_insn_aux_data)), GFP_KERNEL_ACCOUNT | __GFP_ZERO); if (!new_data) return NULL; env->insn_aux_data = new_data; } new_prog = bpf_patch_insn_single(env->prog, off, patch, len); if (IS_ERR(new_prog)) { if (PTR_ERR(new_prog) == -ERANGE) verbose(env, "insn %d cannot be patched due to 16-bit range\n", env->insn_aux_data[off].orig_idx); return NULL; } adjust_insn_aux_data(env, new_prog, off, len); adjust_subprog_starts(env, off, len); adjust_insn_arrays(env, off, len); adjust_poke_descs(new_prog, off, len); return new_prog;} /* * For all jmp insns in a given 'prog' that point to 'tgt_idx' insn adjust the * jump offset by 'delta'. */static int adjust_jmp_off(struct bpf_prog *prog, u32 tgt_idx, u32 delta){ struct bpf_insn *insn = prog->insnsi; u32 insn_cnt = prog->len, i; s32 imm; s16 off; for (i = 0; i < insn_cnt; i++, insn++) { u8 code = insn->code; if (tgt_idx <= i && i < tgt_idx + delta) continue; if ((BPF_CLASS(code) != BPF_JMP && BPF_CLASS(code) != BPF_JMP32) || BPF_OP(code) == BPF_CALL || BPF_OP(code) == BPF_EXIT) continue; if (insn->code == (BPF_JMP32 | BPF_JA)) { if (i + 1 + insn->imm != tgt_idx) continue; if (check_add_overflow(insn->imm, delta, &imm)) return -ERANGE; insn->imm = imm; } else { if (i + 1 + insn->off != tgt_idx) continue; if (check_add_overflow(insn->off, delta, &off)) return -ERANGE; insn->off = off; } } return 0;} static int adjust_subprog_starts_after_remove(struct bpf_verifier_env *env, u32 off, u32 cnt){ int i, j; /* find first prog starting at or after off (first to remove) */ for (i = 0; i < env->subprog_cnt; i++) if (env->subprog_info[i].start >= off) break; /* find first prog starting at or after off + cnt (first to stay) */ for (j = i; j < env->subprog_cnt; j++) if (env->subprog_info[j].start >= off + cnt) break; /* if j doesn't start exactly at off + cnt, we are just removing * the front of previous prog */ if (env->subprog_info[j].start != off + cnt) j--; if (j > i) { struct bpf_prog_aux *aux = env->prog->aux; int move; /* move fake 'exit' subprog as well */ move = env->subprog_cnt + 1 - j; memmove(env->subprog_info + i, env->subprog_info + j, sizeof(*env->subprog_info) * move); env->subprog_cnt -= j - i; /* remove func_info and its aux */ if (aux->func_info) { move = aux->func_info_cnt - j; memmove(aux->func_info + i, aux->func_info + j, sizeof(*aux->func_info) * move); if (aux->func_info_aux) memmove(aux->func_info_aux + i, aux->func_info_aux + j, sizeof(*aux->func_info_aux) * move); aux->func_info_cnt -= j - i; /* func_info->insn_off is set after all code rewrites, * in adjust_btf_func() - no need to adjust */ } } else { /* convert i from "first prog to remove" to "first to adjust" */ if (env->subprog_info[i].start == off) i++; } /* update fake 'exit' subprog as well */ for (; i <= env->subprog_cnt; i++) env->subprog_info[i].start -= cnt; return 0;} static int bpf_adj_linfo_after_remove(struct bpf_verifier_env *env, u32 off, u32 cnt){ struct bpf_prog *prog = env->prog; u32 i, l_off, l_cnt, nr_linfo; struct bpf_line_info *linfo; nr_linfo = prog->aux->nr_linfo; if (!nr_linfo) return 0; linfo = prog->aux->linfo; /* find first line info to remove, count lines to be removed */ for (i = 0; i < nr_linfo; i++) if (linfo[i].insn_off >= off) break; l_off = i; l_cnt = 0; for (; i < nr_linfo; i++) if (linfo[i].insn_off < off + cnt) l_cnt++; else break; /* First live insn doesn't match first live linfo, it needs to "inherit" * last removed linfo. prog is already modified, so prog->len == off * means no live instructions after (tail of the program was removed). */ if (prog->len != off && l_cnt && (i == nr_linfo || linfo[i].insn_off != off + cnt)) { l_cnt--; linfo[--i].insn_off = off + cnt; } /* remove the line info which refer to the removed instructions */ if (l_cnt) { memmove(linfo + l_off, linfo + i, sizeof(*linfo) * (nr_linfo - i)); prog->aux->nr_linfo -= l_cnt; nr_linfo = prog->aux->nr_linfo; } /* pull all linfo[i].insn_off >= off + cnt in by cnt */ for (i = l_off; i < nr_linfo; i++) linfo[i].insn_off -= cnt; /* fix up all subprogs (incl. 'exit') which start >= off */ for (i = 0; i <= env->subprog_cnt; i++) if (env->subprog_info[i].linfo_idx > l_off) { /* program may have started in the removed region but * may not be fully removed */ if (env->subprog_info[i].linfo_idx >= l_off + l_cnt) env->subprog_info[i].linfo_idx -= l_cnt; else env->subprog_info[i].linfo_idx = l_off; } return 0;Lines 1–500 of 2669. Load more
// SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ #include <linux/bpf.h> #include <linux/btf.h> #include <linux/bpf_verifier.h> #include <linux/filter.h> #include <linux/vmalloc.h> #include <linux/bsearch.h> #include <linux/sort.h> #include <linux/perf_event.h> #include <net/xdp.h> #include "disasm.h" #define verbose(env, fmt, args...) bpf_verifier_log_write(env, fmt, ##args) /* * Matches BPF_PROBE_ATOMIC too: bpf_convert_ctx_accesses() rewrites arena * atomics before bpf_opt_subreg_zext_lo32_rnd_hi32() runs. */ static bool is_cmpxchg_insn(const struct bpf_insn *insn) { return BPF_CLASS(insn->code) == BPF_STX && (BPF_MODE(insn->code) == BPF_ATOMIC || BPF_MODE(insn->code) == BPF_PROBE_ATOMIC) && insn->imm == BPF_CMPXCHG; } /* Returns true if 'insn' is an address space cast instruction translated as BPF_ALU op */ static bool is_addr_space_cast32(struct bpf_prog *prog, const struct bpf_insn *insn) { struct bpf_map *arena = (struct bpf_map *)prog->aux->arena; if (insn->code != (BPF_ALU64 | BPF_MOV | BPF_X) || insn->off != BPF_ADDR_SPACE_CAST) return false; /* cast from as(1) to as(0) */ if (insn->imm == 1) return true; /* cast from as(0) to as(1) */ if (insn->imm == 1 << 16) return arena && arena->map_flags & BPF_F_NO_USER_CONV; /* non-BPF_F_NO_USER_CONV cast from as(0) to as(1) should be handled by JIT */ return false; } /* Return the regno defined by the insn, or -1. */ static int insn_def_regno(const struct bpf_insn *insn) { switch (BPF_CLASS(insn->code)) { case BPF_JMP: case BPF_JMP32: case BPF_ST: return -1; case BPF_STX: return bpf_atomic_load_reg(insn); default: return insn->dst_reg; } } /* * For use only in combination with insn_def_regno() >= 0. * Returns TRUE if the destination register operates on 64-bit, * otherwise return FALSE. */ static bool bpf_is_reg64(struct bpf_prog *prog, struct bpf_insn *insn) { u8 class = BPF_CLASS(insn->code); u8 mode = BPF_MODE(insn->code); u8 size = BPF_SIZE(insn->code); u8 op = BPF_OP(insn->code); bool mode_mem; /* subregister endiness swap */ if ((class == BPF_ALU || class == BPF_ALU64) && op == BPF_END && insn->imm != 64) return false; /* w0 += 1 */ if (class == BPF_ALU && op != BPF_END) return false; /* address space casts converted to BPF_ALU, see bpf_do_misc_fixups() */ if (is_addr_space_cast32(prog, insn)) return false; /* non 64-bit, non signed extended loads */ mode_mem = mode == BPF_MEM || mode == BPF_PROBE_MEM || mode == BPF_PROBE_MEM32; if (class == BPF_LDX && mode_mem && size != BPF_DW) return false; /* atomics, see insn_def_regno() */ if (class == BPF_STX && size != BPF_DW) return false; /* both LD_IND and LD_ABS return 32-bit data. */ if (class == BPF_LD && (mode == BPF_IND || mode == BPF_ABS)) return false; /* Conservatively return true at default. */ return true; } /* * Return the 32-bit subregister defined by INSN, or -1 if INSN does not * explicitly define a 32-bit value. */ int bpf_insn_def32(struct bpf_prog *prog, struct bpf_insn *insn) { int dst_reg = insn_def_regno(insn); if (dst_reg < 0 || bpf_is_reg64(prog, insn)) return -1; return dst_reg; } static int kfunc_desc_cmp_by_imm_off(const void *a, const void *b) { const struct bpf_kfunc_desc *d0 = a; const struct bpf_kfunc_desc *d1 = b; if (d0->imm != d1->imm) return d0->imm < d1->imm ? -1 : 1; if (d0->offset != d1->offset) return d0->offset < d1->offset ? -1 : 1; return 0; } const struct btf_func_model * bpf_jit_find_kfunc_model(const struct bpf_prog *prog, const struct bpf_insn *insn) { const struct bpf_kfunc_desc desc = { .imm = insn->imm, .offset = insn->off, }; const struct bpf_kfunc_desc *res; struct bpf_kfunc_desc_tab *tab; tab = prog->aux->kfunc_tab; res = bsearch(&desc, tab->descs, tab->nr_descs, sizeof(tab->descs[0]), kfunc_desc_cmp_by_imm_off); return res ? &res->func_model : NULL; } static int set_kfunc_desc_imm(struct bpf_verifier_env *env, struct bpf_kfunc_desc *desc) { unsigned long call_imm; if (bpf_jit_supports_far_kfunc_call()) { call_imm = desc->func_id; } else { call_imm = BPF_CALL_IMM(desc->addr); /* Check whether the relative offset overflows desc->imm */ if ((unsigned long)(s32)call_imm != call_imm) { verbose(env, "address of kernel func_id %u is out of range\n", desc->func_id); return -EINVAL; } } desc->imm = call_imm; return 0; } static int sort_kfunc_descs_by_imm_off(struct bpf_verifier_env *env) { struct bpf_kfunc_desc_tab *tab; int i, err; tab = env->prog->aux->kfunc_tab; if (!tab) return 0; for (i = 0; i < tab->nr_descs; i++) { err = set_kfunc_desc_imm(env, &tab->descs[i]); if (err) return err; } sort(tab->descs, tab->nr_descs, sizeof(tab->descs[0]), kfunc_desc_cmp_by_imm_off, NULL); return 0; } static int add_kfunc_in_insns(struct bpf_verifier_env *env, struct bpf_insn *insn, int cnt) { int i, ret; for (i = 0; i < cnt; i++, insn++) { if (bpf_pseudo_kfunc_call(insn)) { ret = bpf_add_kfunc_call(env, insn->imm, insn->off); if (ret < 0) return ret; } } return 0; } #ifndef CONFIG_BPF_JIT_ALWAYS_ON static int get_callee_stack_depth(struct bpf_verifier_env *env, const struct bpf_insn *insn, int idx) { int start = idx + insn->imm + 1, subprog; subprog = bpf_find_subprog(env, start); if (verifier_bug_if(subprog < 0, env, "get stack depth: no program at insn %d", start)) return -EFAULT; return env->subprog_info[subprog].stack_depth; } #endif /* single env->prog->insni[off] instruction was replaced with the range * insni[off, off + cnt). Adjust corresponding insn_aux_data by copying * [0, off) and [off, end) to new locations, so the patched range stays zero */ static void adjust_insn_aux_data(struct bpf_verifier_env *env, struct bpf_prog *new_prog, u32 off, u32 cnt) { struct bpf_insn_aux_data *data = env->insn_aux_data; struct bpf_insn *insn = new_prog->insnsi; u32 old_seen = data[off].seen; u32 prog_len; int i; /* aux info at OFF always needs adjustment, no matter fast path * (cnt == 1) is taken or not. There is no guarantee INSN at OFF is the * original insn at old prog. */ data[off].zext_dst = bpf_insn_def32(new_prog, insn + off + cnt - 1) >= 0; if (cnt == 1) return; prog_len = new_prog->len; env->insn_aux_data_len = prog_len; memmove(data + off + cnt - 1, data + off, sizeof(struct bpf_insn_aux_data) * (prog_len - off - cnt + 1)); memset(data + off, 0, sizeof(struct bpf_insn_aux_data) * (cnt - 1)); for (i = off; i < off + cnt - 1; i++) { /* Expand insni[off]'s seen count to the patched range. */ data[i].seen = old_seen; data[i].zext_dst = bpf_insn_def32(new_prog, insn + i) >= 0; } /* * The indirect_target flag of the original instruction was moved to the last of the * new instructions by the above memmove and memset, but the indirect jump target is * actually the first instruction, so move it back. This also matches with the behavior * of bpf_insn_array_adjust(), which preserves xlated_off to point to the first new * instruction. */ if (data[off + cnt - 1].indirect_target) { data[off].indirect_target = 1; data[off + cnt - 1].indirect_target = 0; } } static void adjust_subprog_starts(struct bpf_verifier_env *env, u32 off, u32 len) { int i; if (len == 1) return; /* NOTE: fake 'exit' subprog should be updated as well. */ for (i = 0; i <= env->subprog_cnt; i++) { if (env->subprog_info[i].start <= off) continue; env->subprog_info[i].start += len - 1; } } static void adjust_insn_arrays(struct bpf_verifier_env *env, u32 off, u32 len) { int i; if (len == 1) return; for (i = 0; i < env->insn_array_map_cnt; i++) bpf_insn_array_adjust(env->insn_array_maps[i], off, len); } static void adjust_insn_arrays_after_remove(struct bpf_verifier_env *env, u32 off, u32 len) { int i; for (i = 0; i < env->insn_array_map_cnt; i++) bpf_insn_array_adjust_after_remove(env->insn_array_maps[i], off, len); } static void adjust_poke_descs(struct bpf_prog *prog, u32 off, u32 len) { struct bpf_jit_poke_descriptor *tab = prog->aux->poke_tab; int i, sz = prog->aux->size_poke_tab; struct bpf_jit_poke_descriptor *desc; for (i = 0; i < sz; i++) { desc = &tab[i]; if (desc->insn_idx <= off) continue; desc->insn_idx += len - 1; } } struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off, const struct bpf_insn *patch, u32 len) { struct bpf_prog *new_prog; struct bpf_insn_aux_data *new_data = NULL; if (len > 1) { new_data = vrealloc(env->insn_aux_data, array_size(env->prog->len + len - 1, sizeof(struct bpf_insn_aux_data)), GFP_KERNEL_ACCOUNT | __GFP_ZERO); if (!new_data) return NULL; env->insn_aux_data = new_data; } new_prog = bpf_patch_insn_single(env->prog, off, patch, len); if (IS_ERR(new_prog)) { if (PTR_ERR(new_prog) == -ERANGE) verbose(env, "insn %d cannot be patched due to 16-bit range\n", env->insn_aux_data[off].orig_idx); return NULL; } adjust_insn_aux_data(env, new_prog, off, len); adjust_subprog_starts(env, off, len); adjust_insn_arrays(env, off, len); adjust_poke_descs(new_prog, off, len); return new_prog; } /* * For all jmp insns in a given 'prog' that point to 'tgt_idx' insn adjust the * jump offset by 'delta'. */ static int adjust_jmp_off(struct bpf_prog *prog, u32 tgt_idx, u32 delta) { struct bpf_insn *insn = prog->insnsi; u32 insn_cnt = prog->len, i; s32 imm; s16 off; for (i = 0; i < insn_cnt; i++, insn++) { u8 code = insn->code; if (tgt_idx <= i && i < tgt_idx + delta) continue; if ((BPF_CLASS(code) != BPF_JMP && BPF_CLASS(code) != BPF_JMP32) || BPF_OP(code) == BPF_CALL || BPF_OP(code) == BPF_EXIT) continue; if (insn->code == (BPF_JMP32 | BPF_JA)) { if (i + 1 + insn->imm != tgt_idx) continue; if (check_add_overflow(insn->imm, delta, &imm)) return -ERANGE; insn->imm = imm; } else { if (i + 1 + insn->off != tgt_idx) continue; if (check_add_overflow(insn->off, delta, &off)) return -ERANGE; insn->off = off; } } return 0; } static int adjust_subprog_starts_after_remove(struct bpf_verifier_env *env, u32 off, u32 cnt) { int i, j; /* find first prog starting at or after off (first to remove) */ for (i = 0; i < env->subprog_cnt; i++) if (env->subprog_info[i].start >= off) break; /* find first prog starting at or after off + cnt (first to stay) */ for (j = i; j < env->subprog_cnt; j++) if (env->subprog_info[j].start >= off + cnt) break; /* if j doesn't start exactly at off + cnt, we are just removing * the front of previous prog */ if (env->subprog_info[j].start != off + cnt) j--; if (j > i) { struct bpf_prog_aux *aux = env->prog->aux; int move; /* move fake 'exit' subprog as well */ move = env->subprog_cnt + 1 - j; memmove(env->subprog_info + i, env->subprog_info + j, sizeof(*env->subprog_info) * move); env->subprog_cnt -= j - i; /* remove func_info and its aux */ if (aux->func_info) { move = aux->func_info_cnt - j; memmove(aux->func_info + i, aux->func_info + j, sizeof(*aux->func_info) * move); if (aux->func_info_aux) memmove(aux->func_info_aux + i, aux->func_info_aux + j, sizeof(*aux->func_info_aux) * move); aux->func_info_cnt -= j - i; /* func_info->insn_off is set after all code rewrites, * in adjust_btf_func() - no need to adjust */ } } else { /* convert i from "first prog to remove" to "first to adjust" */ if (env->subprog_info[i].start == off) i++; } /* update fake 'exit' subprog as well */ for (; i <= env->subprog_cnt; i++) env->subprog_info[i].start -= cnt; return 0; } static int bpf_adj_linfo_after_remove(struct bpf_verifier_env *env, u32 off, u32 cnt) { struct bpf_prog *prog = env->prog; u32 i, l_off, l_cnt, nr_linfo; struct bpf_line_info *linfo; nr_linfo = prog->aux->nr_linfo; if (!nr_linfo) return 0; linfo = prog->aux->linfo; /* find first line info to remove, count lines to be removed */ for (i = 0; i < nr_linfo; i++) if (linfo[i].insn_off >= off) break; l_off = i; l_cnt = 0; for (; i < nr_linfo; i++) if (linfo[i].insn_off < off + cnt) l_cnt++; else break; /* First live insn doesn't match first live linfo, it needs to "inherit" * last removed linfo. prog is already modified, so prog->len == off * means no live instructions after (tail of the program was removed). */ if (prog->len != off && l_cnt && (i == nr_linfo || linfo[i].insn_off != off + cnt)) { l_cnt--; linfo[--i].insn_off = off + cnt; } /* remove the line info which refer to the removed instructions */ if (l_cnt) { memmove(linfo + l_off, linfo + i, sizeof(*linfo) * (nr_linfo - i)); prog->aux->nr_linfo -= l_cnt; nr_linfo = prog->aux->nr_linfo; } /* pull all linfo[i].insn_off >= off + cnt in by cnt */ for (i = l_off; i < nr_linfo; i++) linfo[i].insn_off -= cnt; /* fix up all subprogs (incl. 'exit') which start >= off */ for (i = 0; i <= env->subprog_cnt; i++) if (env->subprog_info[i].linfo_idx > l_off) { /* program may have started in the removed region but * may not be fully removed */ if (env->subprog_info[i].linfo_idx >= l_off + l_cnt) env->subprog_info[i].linfo_idx -= l_cnt; else env->subprog_info[i].linfo_idx = l_off; } return 0;