2669 lines · 80.1 KB · C
RawDownload
// 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.