Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'bpf-unify-special-map-field-validation-in-verifier'

Mykyta Yatsenko says:

====================
The BPF verifier validates pointers to special map fields (timers,
workqueues, task_work) through separate functions that share nearly
identical logic. This creates code duplication because of the
inconsistent data structure layout in struct bpf_call_arg_meta struct
bpf_kfunc_call_arg_meta.

This series contains 2 commits:

1. Introduces struct bpf_map_desc to provide a unified representation
for map pointer and uid tracking. Previously, bpf_call_arg_meta used
separate map_ptr and map_uid fields while bpf_kfunc_call_arg_metaused an
anonymous inline struct. This inconsistency made it harder to share
validation code between the two paths.

2. Consolidates the validation logic for BPF_TIMER, BPF_WORKQUEUE, and
BPF_TASK_WORK field types into a single check_map_field_pointer()
function. This eliminates process_wq_func() and process_task_work_func()
entirely, and simplifies process_timer_func() to just the PREEMPT_RT
check before calling the unified validation. The result is fewer
lines of code with clearer structure for future maintenance.

Signed-off-by: Mykyta Yatsenko <yatsenko@meta.com>

Changes in v2:
- Added Signed-off-by to the top commit.
- Link to v1: https://lore.kernel.org/r/20260129-verif_special_fields-v1-0-d310b7f146c8@meta.com
====================

Link: https://patch.msgid.link/20260130-verif_special_fields-v2-0-2c59e637da7d@meta.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+48 -95
+48 -95
kernel/bpf/verifier.c
··· 272 272 insn->src_reg == BPF_PSEUDO_KFUNC_CALL; 273 273 } 274 274 275 + struct bpf_map_desc { 276 + struct bpf_map *ptr; 277 + int uid; 278 + }; 279 + 275 280 struct bpf_call_arg_meta { 276 - struct bpf_map *map_ptr; 281 + struct bpf_map_desc map; 277 282 bool raw_mode; 278 283 bool pkt_access; 279 284 u8 release_regno; ··· 288 283 u64 msize_max_value; 289 284 int ref_obj_id; 290 285 int dynptr_id; 291 - int map_uid; 292 286 int func_id; 293 287 struct btf *btf; 294 288 u32 btf_id; ··· 355 351 u8 spi; 356 352 u8 frameno; 357 353 } iter; 358 - struct { 359 - struct bpf_map *ptr; 360 - int uid; 361 - } map; 354 + struct bpf_map_desc map; 362 355 u64 mem_size; 363 356 }; 364 357 ··· 8610 8609 8611 8610 /* Check if @regno is a pointer to a specific field in a map value */ 8612 8611 static int check_map_field_pointer(struct bpf_verifier_env *env, u32 regno, 8613 - enum btf_field_type field_type) 8612 + enum btf_field_type field_type, 8613 + struct bpf_map_desc *map_desc) 8614 8614 { 8615 8615 struct bpf_reg_state *reg = reg_state(env, regno); 8616 8616 bool is_const = tnum_is_const(reg->var_off); ··· 8654 8652 val + reg->off, struct_name, field_off); 8655 8653 return -EINVAL; 8656 8654 } 8655 + if (map_desc->ptr) { 8656 + verifier_bug(env, "Two map pointers in a %s helper", struct_name); 8657 + return -EFAULT; 8658 + } 8659 + map_desc->uid = reg->map_uid; 8660 + map_desc->ptr = map; 8657 8661 return 0; 8658 8662 } 8659 8663 8660 8664 static int process_timer_func(struct bpf_verifier_env *env, int regno, 8661 8665 struct bpf_call_arg_meta *meta) 8662 8666 { 8663 - struct bpf_reg_state *reg = reg_state(env, regno); 8664 - struct bpf_map *map = reg->map_ptr; 8665 - int err; 8666 - 8667 - err = check_map_field_pointer(env, regno, BPF_TIMER); 8668 - if (err) 8669 - return err; 8670 - 8671 - if (meta->map_ptr) { 8672 - verifier_bug(env, "Two map pointers in a timer helper"); 8673 - return -EFAULT; 8674 - } 8675 8667 if (IS_ENABLED(CONFIG_PREEMPT_RT)) { 8676 8668 verbose(env, "bpf_timer cannot be used for PREEMPT_RT.\n"); 8677 8669 return -EOPNOTSUPP; 8678 8670 } 8679 - meta->map_uid = reg->map_uid; 8680 - meta->map_ptr = map; 8681 - return 0; 8682 - } 8683 - 8684 - static int process_wq_func(struct bpf_verifier_env *env, int regno, 8685 - struct bpf_kfunc_call_arg_meta *meta) 8686 - { 8687 - struct bpf_reg_state *reg = reg_state(env, regno); 8688 - struct bpf_map *map = reg->map_ptr; 8689 - int err; 8690 - 8691 - err = check_map_field_pointer(env, regno, BPF_WORKQUEUE); 8692 - if (err) 8693 - return err; 8694 - 8695 - if (meta->map.ptr) { 8696 - verifier_bug(env, "Two map pointers in a bpf_wq helper"); 8697 - return -EFAULT; 8698 - } 8699 - 8700 - meta->map.uid = reg->map_uid; 8701 - meta->map.ptr = map; 8702 - return 0; 8703 - } 8704 - 8705 - static int process_task_work_func(struct bpf_verifier_env *env, int regno, 8706 - struct bpf_kfunc_call_arg_meta *meta) 8707 - { 8708 - struct bpf_reg_state *reg = reg_state(env, regno); 8709 - struct bpf_map *map = reg->map_ptr; 8710 - int err; 8711 - 8712 - err = check_map_field_pointer(env, regno, BPF_TASK_WORK); 8713 - if (err) 8714 - return err; 8715 - 8716 - if (meta->map.ptr) { 8717 - verifier_bug(env, "Two map pointers in a bpf_task_work helper"); 8718 - return -EFAULT; 8719 - } 8720 - meta->map.uid = reg->map_uid; 8721 - meta->map.ptr = map; 8722 - return 0; 8671 + return check_map_field_pointer(env, regno, BPF_TIMER, &meta->map); 8723 8672 } 8724 8673 8725 8674 static int process_kptr_func(struct bpf_verifier_env *env, int regno, ··· 8692 8739 return -EINVAL; 8693 8740 } 8694 8741 rec = map_ptr->record; 8695 - meta->map_ptr = map_ptr; 8742 + meta->map.ptr = map_ptr; 8696 8743 } 8697 8744 8698 8745 if (!tnum_is_const(reg->var_off)) { ··· 9199 9246 const struct bpf_call_arg_meta *meta, 9200 9247 enum bpf_arg_type *arg_type) 9201 9248 { 9202 - if (!meta->map_ptr) { 9249 + if (!meta->map.ptr) { 9203 9250 /* kernel subsystem misconfigured verifier */ 9204 9251 verifier_bug(env, "invalid map_ptr to access map->type"); 9205 9252 return -EFAULT; 9206 9253 } 9207 9254 9208 - switch (meta->map_ptr->map_type) { 9255 + switch (meta->map.ptr->map_type) { 9209 9256 case BPF_MAP_TYPE_SOCKMAP: 9210 9257 case BPF_MAP_TYPE_SOCKHASH: 9211 9258 if (*arg_type == ARG_PTR_TO_MAP_VALUE) { ··· 9859 9906 switch (base_type(arg_type)) { 9860 9907 case ARG_CONST_MAP_PTR: 9861 9908 /* bpf_map_xxx(map_ptr) call: remember that map_ptr */ 9862 - if (meta->map_ptr) { 9909 + if (meta->map.ptr) { 9863 9910 /* Use map_uid (which is unique id of inner map) to reject: 9864 9911 * inner_map1 = bpf_map_lookup_elem(outer_map, key1) 9865 9912 * inner_map2 = bpf_map_lookup_elem(outer_map, key2) ··· 9872 9919 * 9873 9920 * Comparing map_ptr is enough to distinguish normal and outer maps. 9874 9921 */ 9875 - if (meta->map_ptr != reg->map_ptr || 9876 - meta->map_uid != reg->map_uid) { 9922 + if (meta->map.ptr != reg->map_ptr || 9923 + meta->map.uid != reg->map_uid) { 9877 9924 verbose(env, 9878 9925 "timer pointer in R1 map_uid=%d doesn't match map pointer in R2 map_uid=%d\n", 9879 - meta->map_uid, reg->map_uid); 9926 + meta->map.uid, reg->map_uid); 9880 9927 return -EINVAL; 9881 9928 } 9882 9929 } 9883 - meta->map_ptr = reg->map_ptr; 9884 - meta->map_uid = reg->map_uid; 9930 + meta->map.ptr = reg->map_ptr; 9931 + meta->map.uid = reg->map_uid; 9885 9932 break; 9886 9933 case ARG_PTR_TO_MAP_KEY: 9887 9934 /* bpf_map_xxx(..., map_ptr, ..., key) call: 9888 9935 * check that [key, key + map->key_size) are within 9889 9936 * stack limits and initialized 9890 9937 */ 9891 - if (!meta->map_ptr) { 9938 + if (!meta->map.ptr) { 9892 9939 /* in function declaration map_ptr must come before 9893 9940 * map_key, so that it's verified and known before 9894 9941 * we have to check map_key here. Otherwise it means ··· 9897 9944 verifier_bug(env, "invalid map_ptr to access map->key"); 9898 9945 return -EFAULT; 9899 9946 } 9900 - key_size = meta->map_ptr->key_size; 9947 + key_size = meta->map.ptr->key_size; 9901 9948 err = check_helper_mem_access(env, regno, key_size, BPF_READ, false, NULL); 9902 9949 if (err) 9903 9950 return err; 9904 - if (can_elide_value_nullness(meta->map_ptr->map_type)) { 9951 + if (can_elide_value_nullness(meta->map.ptr->map_type)) { 9905 9952 err = get_constant_map_key(env, reg, key_size, &meta->const_map_key); 9906 9953 if (err < 0) { 9907 9954 meta->const_map_key = -1; ··· 9919 9966 /* bpf_map_xxx(..., map_ptr, ..., value) call: 9920 9967 * check [value, value + map->value_size) validity 9921 9968 */ 9922 - if (!meta->map_ptr) { 9969 + if (!meta->map.ptr) { 9923 9970 /* kernel subsystem misconfigured verifier */ 9924 9971 verifier_bug(env, "invalid map_ptr to access map->value"); 9925 9972 return -EFAULT; 9926 9973 } 9927 9974 meta->raw_mode = arg_type & MEM_UNINIT; 9928 - err = check_helper_mem_access(env, regno, meta->map_ptr->value_size, 9975 + err = check_helper_mem_access(env, regno, meta->map.ptr->value_size, 9929 9976 arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ, 9930 9977 false, meta); 9931 9978 break; ··· 11263 11310 int func_id, int insn_idx) 11264 11311 { 11265 11312 struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx]; 11266 - struct bpf_map *map = meta->map_ptr; 11313 + struct bpf_map *map = meta->map.ptr; 11267 11314 11268 11315 if (func_id != BPF_FUNC_tail_call && 11269 11316 func_id != BPF_FUNC_map_lookup_elem && ··· 11296 11343 } 11297 11344 11298 11345 if (!aux->map_ptr_state.map_ptr) 11299 - bpf_map_ptr_store(aux, meta->map_ptr, 11300 - !meta->map_ptr->bypass_spec_v1, false); 11301 - else if (aux->map_ptr_state.map_ptr != meta->map_ptr) 11302 - bpf_map_ptr_store(aux, meta->map_ptr, 11303 - !meta->map_ptr->bypass_spec_v1, true); 11346 + bpf_map_ptr_store(aux, meta->map.ptr, 11347 + !meta->map.ptr->bypass_spec_v1, false); 11348 + else if (aux->map_ptr_state.map_ptr != meta->map.ptr) 11349 + bpf_map_ptr_store(aux, meta->map.ptr, 11350 + !meta->map.ptr->bypass_spec_v1, true); 11304 11351 return 0; 11305 11352 } 11306 11353 ··· 11310 11357 { 11311 11358 struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx]; 11312 11359 struct bpf_reg_state *reg; 11313 - struct bpf_map *map = meta->map_ptr; 11360 + struct bpf_map *map = meta->map.ptr; 11314 11361 u64 val, max; 11315 11362 int err; 11316 11363 ··· 11866 11913 * can check 'value_size' boundary of memory access 11867 11914 * to map element returned from bpf_map_lookup_elem() 11868 11915 */ 11869 - if (meta.map_ptr == NULL) { 11916 + if (meta.map.ptr == NULL) { 11870 11917 verifier_bug(env, "unexpected null map_ptr"); 11871 11918 return -EFAULT; 11872 11919 } 11873 11920 11874 11921 if (func_id == BPF_FUNC_map_lookup_elem && 11875 - can_elide_value_nullness(meta.map_ptr->map_type) && 11922 + can_elide_value_nullness(meta.map.ptr->map_type) && 11876 11923 meta.const_map_key >= 0 && 11877 - meta.const_map_key < meta.map_ptr->max_entries) 11924 + meta.const_map_key < meta.map.ptr->max_entries) 11878 11925 ret_flag &= ~PTR_MAYBE_NULL; 11879 11926 11880 - regs[BPF_REG_0].map_ptr = meta.map_ptr; 11881 - regs[BPF_REG_0].map_uid = meta.map_uid; 11927 + regs[BPF_REG_0].map_ptr = meta.map.ptr; 11928 + regs[BPF_REG_0].map_uid = meta.map.uid; 11882 11929 regs[BPF_REG_0].type = PTR_TO_MAP_VALUE | ret_flag; 11883 11930 if (!type_may_be_null(ret_flag) && 11884 - btf_record_has_field(meta.map_ptr->record, BPF_SPIN_LOCK | BPF_RES_SPIN_LOCK)) { 11931 + btf_record_has_field(meta.map.ptr->record, BPF_SPIN_LOCK | BPF_RES_SPIN_LOCK)) { 11885 11932 regs[BPF_REG_0].id = ++env->id_gen; 11886 11933 } 11887 11934 break; ··· 11984 12031 if (type_may_be_null(regs[BPF_REG_0].type)) 11985 12032 regs[BPF_REG_0].id = ++env->id_gen; 11986 12033 11987 - if (helper_multiple_ref_obj_use(func_id, meta.map_ptr)) { 12034 + if (helper_multiple_ref_obj_use(func_id, meta.map.ptr)) { 11988 12035 verifier_bug(env, "func %s#%d sets ref_obj_id more than once", 11989 12036 func_id_name(func_id), func_id); 11990 12037 return -EFAULT; ··· 11996 12043 if (is_ptr_cast_function(func_id) || is_dynptr_ref_function(func_id)) { 11997 12044 /* For release_reference() */ 11998 12045 regs[BPF_REG_0].ref_obj_id = meta.ref_obj_id; 11999 - } else if (is_acquire_function(func_id, meta.map_ptr)) { 12046 + } else if (is_acquire_function(func_id, meta.map.ptr)) { 12000 12047 int id = acquire_reference(env, insn_idx); 12001 12048 12002 12049 if (id < 0) ··· 12011 12058 if (err) 12012 12059 return err; 12013 12060 12014 - err = check_map_func_compatibility(env, meta.map_ptr, func_id); 12061 + err = check_map_func_compatibility(env, meta.map.ptr, func_id); 12015 12062 if (err) 12016 12063 return err; 12017 12064 ··· 13706 13753 verbose(env, "arg#%d doesn't point to a map value\n", i); 13707 13754 return -EINVAL; 13708 13755 } 13709 - ret = process_wq_func(env, regno, meta); 13756 + ret = check_map_field_pointer(env, regno, BPF_WORKQUEUE, &meta->map); 13710 13757 if (ret < 0) 13711 13758 return ret; 13712 13759 break; ··· 13715 13762 verbose(env, "arg#%d doesn't point to a map value\n", i); 13716 13763 return -EINVAL; 13717 13764 } 13718 - ret = process_task_work_func(env, regno, meta); 13765 + ret = check_map_field_pointer(env, regno, BPF_TASK_WORK, &meta->map); 13719 13766 if (ret < 0) 13720 13767 return ret; 13721 13768 break;