Re: [PATCH bpf-next v1 3/4] bpf: use SCC info instead of loop_entry

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Eduard,

kernel test robot noticed the following build warnings:

[auto build test WARNING on bpf-next/master]

url:    https://github.com/intel-lab-lkp/linux/commits/Eduard-Zingerman/bpf-compute-SCCs-in-program-control-flow-graph/20250426-184824
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
patch link:    https://lore.kernel.org/r/20250426104634.744077-4-eddyz87%40gmail.com
patch subject: [PATCH bpf-next v1 3/4] bpf: use SCC info instead of loop_entry
config: riscv-randconfig-001-20250426 (https://download.01.org/0day-ci/archive/20250426/202504262235.h5B7vJiB-lkp@xxxxxxxxx/config)
compiler: riscv64-linux-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250426/202504262235.h5B7vJiB-lkp@xxxxxxxxx/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@xxxxxxxxx>
| Closes: https://lore.kernel.org/oe-kbuild-all/202504262235.h5B7vJiB-lkp@xxxxxxxxx/

All warnings (new ones prefixed by >>):

   kernel/bpf/verifier.c: In function 'mark_all_regs_read_and_precise':
>> kernel/bpf/verifier.c:18238:13: warning: variable 'insn_idx' set but not used [-Wunused-but-set-variable]
   18238 |         u32 insn_idx;
         |             ^~~~~~~~
   At top level:
   cc1: note: unrecognized command-line option '-Wno-unterminated-string-initialization' may have been intended to silence earlier diagnostics


vim +/insn_idx +18238 kernel/bpf/verifier.c

 18154	
 18155	/* Open coded iterators introduce loops in the verifier state graph.
 18156	 * State graph loops can result in incomplete read and precision marks
 18157	 * on individual states. E.g. consider the following states graph:
 18158	 *
 18159	 *  .-> A --.  Assume the states are visited in the order A, B, C.
 18160	 *  |   |   |  Assume that state B reaches a state equivalent to state A.
 18161	 *  |   v   v  At this point, state C has not been processed yet,
 18162	 *  '-- B   C  so state A does not have any read or precision marks from C yet.
 18163	 *             As a result, these marks won't be propagated to B.
 18164	 *
 18165	 * If the marks on B are incomplete, it would be unsafe to use it in
 18166	 * states_equal() checks.
 18167	 *
 18168	 * To avoid this safety issue, and since states with incomplete read
 18169	 * marks can only occur within control flow graph loops, the verifier
 18170	 * assumes that any state with bpf_verifier_state->insn_idx residing
 18171	 * in a strongly connected component (SCC) has read and precision
 18172	 * marks for all registers. This assumption is enforced by the
 18173	 * function mark_all_regs_read_and_precise(), which assigns
 18174	 * corresponding marks.
 18175	 *
 18176	 * An intuitive point to call mark_all_regs_read_and_precise() would
 18177	 * be when a new state is created in is_state_visited().
 18178	 * However, doing so would interfere with widen_imprecise_scalars(),
 18179	 * which widens scalars in the current state after checking registers in a
 18180	 * parent state. Registers are not widened if they are marked as precise
 18181	 * in the parent state.
 18182	 *
 18183	 * To avoid interfering with widening logic,
 18184	 * a call to mark_all_regs_read_and_precise() for state is postponed
 18185	 * until no widening is possible in any descendant of state S.
 18186	 *
 18187	 * Another intuitive spot to call mark_all_regs_read_and_precise()
 18188	 * would be in update_branch_counts() when S's branches counter
 18189	 * reaches 0. However, this falls short in the following case:
 18190	 *
 18191	 *	sum = 0
 18192	 *	bpf_repeat(10) {                              // a
 18193	 *		if (unlikely(bpf_get_prandom_u32()))  // b
 18194	 *			sum += 1;
 18195	 *		if (bpf_get_prandom_u32())            // c
 18196	 *			asm volatile ("");
 18197	 *		asm volatile ("goto +0;");            // d
 18198	 *	}
 18199	 *
 18200	 * Here a checkpoint is created at (d) with {sum=0} and the branch counter
 18201	 * for (d) reaches 0, so 'sum' would be marked precise.
 18202	 * When second branch of (c) reaches (d), checkpoint would be hit,
 18203	 * and the precision mark for 'sum' propagated to (a).
 18204	 * When the second branch of (b) reaches (a), the state would be {sum=1},
 18205	 * no widening would occur, causing verification to continue forever.
 18206	 *
 18207	 * To avoid such premature precision markings, the verifier postpones
 18208	 * the call to mark_all_regs_read_and_precise() for state S even further.
 18209	 * Suppose state P is a [grand]parent of state S and is the first state
 18210	 * in the current state chain with state->insn_idx within current SCC.
 18211	 * mark_all_regs_read_and_precise() for state S is only called once P
 18212	 * is fully explored.
 18213	 *
 18214	 * The struct 'bpf_scc_info' is used to track this condition:
 18215	 * - bpf_scc_info->branches counts how many states currently
 18216	 *   in env->cur_state or env->head originate from this SCC;
 18217	 * - bpf_scc_info->scc_epoch counts how many times 'branches'
 18218	 *   has reached zero;
 18219	 * - bpf_verifier_state->scc_epoch records the epoch of the SCC
 18220	 *   corresponding to bpf_verifier_state->insn_idx at the moment
 18221	 *   of state creation.
 18222	 *
 18223	 * Functions parent_scc_enter() and parent_scc_exit() maintain the
 18224	 * bpf_scc_info->{branches,scc_epoch} counters.
 18225	 *
 18226	 * bpf_scc_info->branches reaching zero indicates that state P is
 18227	 * fully explored. Its descendants residing in the same SCC have
 18228	 * state->scc_epoch == scc_info->scc_epoch. parent_scc_exit()
 18229	 * increments scc_info->scc_epoch, allowing clean_live_states() to
 18230	 * detect these states and apply mark_all_regs_read_and_precise().
 18231	 */
 18232	static void mark_all_regs_read_and_precise(struct bpf_verifier_env *env,
 18233						   struct bpf_verifier_state *st)
 18234	{
 18235		struct bpf_func_state *func;
 18236		struct bpf_reg_state *reg;
 18237		u16 live_regs;
 18238		u32 insn_idx;
 18239		int i, j;
 18240	
 18241		for (i = 0; i <= st->curframe; i++) {
 18242			insn_idx = frame_insn_idx(st, i);
 18243			live_regs = env->insn_aux_data[st->insn_idx].live_regs_before;
 18244			func = st->frame[i];
 18245			for (j = 0; j < BPF_REG_FP; j++) {
 18246				reg = &func->regs[j];
 18247				if (!(BIT(j) & live_regs) || reg->type == NOT_INIT)
 18248					continue;
 18249				reg->live |= REG_LIVE_READ64;
 18250				if (reg->type == SCALAR_VALUE && !is_reg_unbounded(reg))
 18251					reg->precise = true;
 18252			}
 18253			for (j = 0; j < func->allocated_stack / BPF_REG_SIZE; j++) {
 18254				reg = &func->stack[j].spilled_ptr;
 18255				reg->live |= REG_LIVE_READ64;
 18256				if (is_spilled_reg(&func->stack[j]) &&
 18257				    reg->type == SCALAR_VALUE && !is_reg_unbounded(reg))
 18258					reg->precise = true;
 18259			}
 18260		}
 18261	}
 18262	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki




[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux