diff mbox series

[v12,bpf-next,09/10] bpf: Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr

Message ID 20230226085120.3907863-10-joannelkoong@gmail.com (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series Add skb + xdp dynptrs | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR success PR summary
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ${{ matrix.test }} on ${{ matrix.arch }} with ${{ matrix.toolchain }}
bpf/vmtest-bpf-next-VM_Test-2 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-3 fail Logs for build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-4 fail Logs for build for aarch64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-5 fail Logs for build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-6 fail Logs for build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-7 fail Logs for build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-8 success Logs for llvm-toolchain
bpf/vmtest-bpf-next-VM_Test-9 success Logs for set-matrix
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 1753 this patch: 1761
netdev/cc_maintainers warning 13 maintainers not CCed: pabeni@redhat.com davem@davemloft.net jolsa@kernel.org john.fastabend@gmail.com martin.lau@linux.dev yhs@fb.com kpsingh@kernel.org song@kernel.org haoluo@google.com edumazet@google.com kuba@kernel.org hawk@kernel.org sdf@google.com
netdev/build_clang fail Errors and warnings before: 164 this patch: 166
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 1750 this patch: 1758
netdev/checkpatch warning CHECK: Please don't use multiple blank lines WARNING: Missing a blank line after declarations WARNING: line length of 100 exceeds 80 columns WARNING: line length of 110 exceeds 80 columns WARNING: line length of 112 exceeds 80 columns WARNING: line length of 114 exceeds 80 columns WARNING: line length of 120 exceeds 80 columns WARNING: line length of 124 exceeds 80 columns WARNING: line length of 81 exceeds 80 columns WARNING: line length of 82 exceeds 80 columns WARNING: line length of 83 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns WARNING: line length of 89 exceeds 80 columns WARNING: line length of 90 exceeds 80 columns WARNING: line length of 93 exceeds 80 columns WARNING: line length of 94 exceeds 80 columns WARNING: line length of 96 exceeds 80 columns WARNING: line length of 98 exceeds 80 columns
netdev/kdoc fail Errors and warnings before: 3 this patch: 5
netdev/source_inline success Was 0 now: 0

Commit Message

Joanne Koong Feb. 26, 2023, 8:51 a.m. UTC
Two new kfuncs are added, bpf_dynptr_slice and bpf_dynptr_slice_rdwr.
The user must pass in a buffer to store the contents of the data slice
if a direct pointer to the data cannot be obtained.

For skb and xdp type dynptrs, these two APIs are the only way to obtain
a data slice. However, for other types of dynptrs, there is no
difference between bpf_dynptr_slice(_rdwr) and bpf_dynptr_data.

For skb type dynptrs, the data is copied into the user provided buffer
if any of the data is not in the linear portion of the skb. For xdp type
dynptrs, the data is copied into the user provided buffer if the data is
between xdp frags.

If the skb is cloned and a call to bpf_dynptr_data_rdwr is made, then
the skb will be uncloned (see bpf_unclone_prologue()).

Please note that any bpf_dynptr_write() automatically invalidates any prior
data slices of the skb dynptr. This is because the skb may be cloned or
may need to pull its paged buffer into the head. As such, any
bpf_dynptr_write() will automatically have its prior data slices
invalidated, even if the write is to data in the skb head of an uncloned
skb. Please note as well that any other helper calls that change the
underlying packet buffer (eg bpf_skb_pull_data()) invalidates any data
slices of the skb dynptr as well, for the same reasons.

Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
---
 include/linux/filter.h         |  14 ++++
 include/uapi/linux/bpf.h       |   5 ++
 kernel/bpf/helpers.c           | 138 +++++++++++++++++++++++++++++++++
 kernel/bpf/verifier.c          | 125 ++++++++++++++++++++++++++++-
 net/core/filter.c              |   6 +-
 tools/include/uapi/linux/bpf.h |   5 ++
 6 files changed, 286 insertions(+), 7 deletions(-)

Comments

kernel test robot Feb. 26, 2023, 10:03 a.m. UTC | #1
Hi Joanne,

Thank you for the patch! Perhaps something to improve:

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

url:    https://github.com/intel-lab-lkp/linux/commits/Joanne-Koong/bpf-Support-sk_buff-and-xdp_buff-as-valid-kfunc-arg-types/20230226-165406
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
patch link:    https://lore.kernel.org/r/20230226085120.3907863-10-joannelkoong%40gmail.com
patch subject: [PATCH v12 bpf-next 09/10] bpf: Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr
config: hexagon-randconfig-r026-20230226 (https://download.01.org/0day-ci/archive/20230226/202302261742.iOSFw9wm-lkp@intel.com/config)
compiler: clang version 17.0.0 (https://github.com/llvm/llvm-project db89896bbbd2251fff457699635acbbedeead27f)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/bc5a61c43c72539ef11e6435a168bf240a186ac1
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Joanne-Koong/bpf-Support-sk_buff-and-xdp_buff-as-valid-kfunc-arg-types/20230226-165406
        git checkout bc5a61c43c72539ef11e6435a168bf240a186ac1
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon SHELL=/bin/bash kernel/bpf/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202302261742.iOSFw9wm-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from kernel/bpf/verifier.c:7:
   In file included from include/linux/bpf-cgroup.h:5:
   In file included from include/linux/bpf.h:31:
   In file included from include/linux/memcontrol.h:13:
   In file included from include/linux/cgroup.h:26:
   In file included from include/linux/kernel_stat.h:9:
   In file included from include/linux/interrupt.h:11:
   In file included from include/linux/hardirq.h:11:
   In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1:
   In file included from include/asm-generic/hardirq.h:17:
   In file included from include/linux/irq.h:20:
   In file included from include/linux/io.h:13:
   In file included from arch/hexagon/include/asm/io.h:334:
   include/asm-generic/io.h:547:31: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           val = __raw_readb(PCI_IOBASE + addr);
                             ~~~~~~~~~~ ^
   include/asm-generic/io.h:560:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           val = __le16_to_cpu((__le16 __force)__raw_readw(PCI_IOBASE + addr));
                                                           ~~~~~~~~~~ ^
   include/uapi/linux/byteorder/little_endian.h:37:51: note: expanded from macro '__le16_to_cpu'
   #define __le16_to_cpu(x) ((__force __u16)(__le16)(x))
                                                     ^
   In file included from kernel/bpf/verifier.c:7:
   In file included from include/linux/bpf-cgroup.h:5:
   In file included from include/linux/bpf.h:31:
   In file included from include/linux/memcontrol.h:13:
   In file included from include/linux/cgroup.h:26:
   In file included from include/linux/kernel_stat.h:9:
   In file included from include/linux/interrupt.h:11:
   In file included from include/linux/hardirq.h:11:
   In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1:
   In file included from include/asm-generic/hardirq.h:17:
   In file included from include/linux/irq.h:20:
   In file included from include/linux/io.h:13:
   In file included from arch/hexagon/include/asm/io.h:334:
   include/asm-generic/io.h:573:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           val = __le32_to_cpu((__le32 __force)__raw_readl(PCI_IOBASE + addr));
                                                           ~~~~~~~~~~ ^
   include/uapi/linux/byteorder/little_endian.h:35:51: note: expanded from macro '__le32_to_cpu'
   #define __le32_to_cpu(x) ((__force __u32)(__le32)(x))
                                                     ^
   In file included from kernel/bpf/verifier.c:7:
   In file included from include/linux/bpf-cgroup.h:5:
   In file included from include/linux/bpf.h:31:
   In file included from include/linux/memcontrol.h:13:
   In file included from include/linux/cgroup.h:26:
   In file included from include/linux/kernel_stat.h:9:
   In file included from include/linux/interrupt.h:11:
   In file included from include/linux/hardirq.h:11:
   In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1:
   In file included from include/asm-generic/hardirq.h:17:
   In file included from include/linux/irq.h:20:
   In file included from include/linux/io.h:13:
   In file included from arch/hexagon/include/asm/io.h:334:
   include/asm-generic/io.h:584:33: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           __raw_writeb(value, PCI_IOBASE + addr);
                               ~~~~~~~~~~ ^
   include/asm-generic/io.h:594:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           __raw_writew((u16 __force)cpu_to_le16(value), PCI_IOBASE + addr);
                                                         ~~~~~~~~~~ ^
   include/asm-generic/io.h:604:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           __raw_writel((u32 __force)cpu_to_le32(value), PCI_IOBASE + addr);
                                                         ~~~~~~~~~~ ^
   kernel/bpf/verifier.c:6253:5: warning: no previous prototype for function 'process_dynptr_func' [-Wmissing-prototypes]
   int process_dynptr_func(struct bpf_verifier_env *env, int regno, int insn_idx,
       ^
   kernel/bpf/verifier.c:6253:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   int process_dynptr_func(struct bpf_verifier_env *env, int regno, int insn_idx,
   ^
   static 
   kernel/bpf/verifier.c:9907:4: error: expected expression
                           struct bpf_reg_state *size_reg = &regs[regno + 1];
                           ^
   kernel/bpf/verifier.c:9910:40: error: use of undeclared identifier 'size_reg'; did you mean 'size_arg'?
                           ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1);
                                                               ^~~~~~~~
                                                               size_arg
   kernel/bpf/verifier.c:9908:28: note: 'size_arg' declared here
                           const struct btf_param *size_arg = &args[i + 1];
                                                   ^
   kernel/bpf/verifier.c:9916:57: error: use of undeclared identifier 'size_reg'; did you mean 'size_arg'?
                           if (is_kfunc_arg_const_mem_size(meta->btf, size_arg, size_reg)) {
                                                                                ^~~~~~~~
                                                                                size_arg
   kernel/bpf/verifier.c:9908:28: note: 'size_arg' declared here
                           const struct btf_param *size_arg = &args[i + 1];
                                                   ^
   kernel/bpf/verifier.c:9921:24: error: use of undeclared identifier 'size_reg'
                                   if (!tnum_is_const(size_reg->var_off)) {
                                                      ^
   kernel/bpf/verifier.c:9926:32: error: use of undeclared identifier 'size_reg'
                                   meta->arg_constant.value = size_reg->var_off.value;
                                                              ^
>> kernel/bpf/verifier.c:9908:28: warning: mixing declarations and code is incompatible with standards before C99 [-Wdeclaration-after-statement]
                           const struct btf_param *size_arg = &args[i + 1];
                                                   ^
   8 warnings and 5 errors generated.


vim +9908 kernel/bpf/verifier.c

  9584	
  9585	static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_arg_meta *meta,
  9586				    int insn_idx)
  9587	{
  9588		const char *func_name = meta->func_name, *ref_tname;
  9589		const struct btf *btf = meta->btf;
  9590		const struct btf_param *args;
  9591		u32 i, nargs;
  9592		int ret;
  9593	
  9594		args = (const struct btf_param *)(meta->func_proto + 1);
  9595		nargs = btf_type_vlen(meta->func_proto);
  9596		if (nargs > MAX_BPF_FUNC_REG_ARGS) {
  9597			verbose(env, "Function %s has %d > %d args\n", func_name, nargs,
  9598				MAX_BPF_FUNC_REG_ARGS);
  9599			return -EINVAL;
  9600		}
  9601	
  9602		/* Check that BTF function arguments match actual types that the
  9603		 * verifier sees.
  9604		 */
  9605		for (i = 0; i < nargs; i++) {
  9606			struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[i + 1];
  9607			const struct btf_type *t, *ref_t, *resolve_ret;
  9608			enum bpf_arg_type arg_type = ARG_DONTCARE;
  9609			u32 regno = i + 1, ref_id, type_size;
  9610			bool is_ret_buf_sz = false;
  9611			int kf_arg_type;
  9612	
  9613			t = btf_type_skip_modifiers(btf, args[i].type, NULL);
  9614	
  9615			if (is_kfunc_arg_ignore(btf, &args[i]))
  9616				continue;
  9617	
  9618			if (btf_type_is_scalar(t)) {
  9619				if (reg->type != SCALAR_VALUE) {
  9620					verbose(env, "R%d is not a scalar\n", regno);
  9621					return -EINVAL;
  9622				}
  9623	
  9624				if (is_kfunc_arg_constant(meta->btf, &args[i])) {
  9625					if (meta->arg_constant.found) {
  9626						verbose(env, "verifier internal error: only one constant argument permitted\n");
  9627						return -EFAULT;
  9628					}
  9629					if (!tnum_is_const(reg->var_off)) {
  9630						verbose(env, "R%d must be a known constant\n", regno);
  9631						return -EINVAL;
  9632					}
  9633					ret = mark_chain_precision(env, regno);
  9634					if (ret < 0)
  9635						return ret;
  9636					meta->arg_constant.found = true;
  9637					meta->arg_constant.value = reg->var_off.value;
  9638				} else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size")) {
  9639					meta->r0_rdonly = true;
  9640					is_ret_buf_sz = true;
  9641				} else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdwr_buf_size")) {
  9642					is_ret_buf_sz = true;
  9643				}
  9644	
  9645				if (is_ret_buf_sz) {
  9646					if (meta->r0_size) {
  9647						verbose(env, "2 or more rdonly/rdwr_buf_size parameters for kfunc");
  9648						return -EINVAL;
  9649					}
  9650	
  9651					if (!tnum_is_const(reg->var_off)) {
  9652						verbose(env, "R%d is not a const\n", regno);
  9653						return -EINVAL;
  9654					}
  9655	
  9656					meta->r0_size = reg->var_off.value;
  9657					ret = mark_chain_precision(env, regno);
  9658					if (ret)
  9659						return ret;
  9660				}
  9661				continue;
  9662			}
  9663	
  9664			if (!btf_type_is_ptr(t)) {
  9665				verbose(env, "Unrecognized arg#%d type %s\n", i, btf_type_str(t));
  9666				return -EINVAL;
  9667			}
  9668	
  9669			if (is_kfunc_trusted_args(meta) &&
  9670			    (register_is_null(reg) || type_may_be_null(reg->type))) {
  9671				verbose(env, "Possibly NULL pointer passed to trusted arg%d\n", i);
  9672				return -EACCES;
  9673			}
  9674	
  9675			if (reg->ref_obj_id) {
  9676				if (is_kfunc_release(meta) && meta->ref_obj_id) {
  9677					verbose(env, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n",
  9678						regno, reg->ref_obj_id,
  9679						meta->ref_obj_id);
  9680					return -EFAULT;
  9681				}
  9682				meta->ref_obj_id = reg->ref_obj_id;
  9683				if (is_kfunc_release(meta))
  9684					meta->release_regno = regno;
  9685			}
  9686	
  9687			ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
  9688			ref_tname = btf_name_by_offset(btf, ref_t->name_off);
  9689	
  9690			kf_arg_type = get_kfunc_ptr_arg_type(env, meta, t, ref_t, ref_tname, args, i, nargs);
  9691			if (kf_arg_type < 0)
  9692				return kf_arg_type;
  9693	
  9694			switch (kf_arg_type) {
  9695			case KF_ARG_PTR_TO_ALLOC_BTF_ID:
  9696			case KF_ARG_PTR_TO_BTF_ID:
  9697				if (!is_kfunc_trusted_args(meta) && !is_kfunc_rcu(meta))
  9698					break;
  9699	
  9700				if (!is_trusted_reg(reg)) {
  9701					if (!is_kfunc_rcu(meta)) {
  9702						verbose(env, "R%d must be referenced or trusted\n", regno);
  9703						return -EINVAL;
  9704					}
  9705					if (!is_rcu_reg(reg)) {
  9706						verbose(env, "R%d must be a rcu pointer\n", regno);
  9707						return -EINVAL;
  9708					}
  9709				}
  9710	
  9711				fallthrough;
  9712			case KF_ARG_PTR_TO_CTX:
  9713				/* Trusted arguments have the same offset checks as release arguments */
  9714				arg_type |= OBJ_RELEASE;
  9715				break;
  9716			case KF_ARG_PTR_TO_KPTR:
  9717			case KF_ARG_PTR_TO_DYNPTR:
  9718			case KF_ARG_PTR_TO_LIST_HEAD:
  9719			case KF_ARG_PTR_TO_LIST_NODE:
  9720			case KF_ARG_PTR_TO_RB_ROOT:
  9721			case KF_ARG_PTR_TO_RB_NODE:
  9722			case KF_ARG_PTR_TO_MEM:
  9723			case KF_ARG_PTR_TO_MEM_SIZE:
  9724			case KF_ARG_PTR_TO_CALLBACK:
  9725				/* Trusted by default */
  9726				break;
  9727			default:
  9728				WARN_ON_ONCE(1);
  9729				return -EFAULT;
  9730			}
  9731	
  9732			if (is_kfunc_release(meta) && reg->ref_obj_id)
  9733				arg_type |= OBJ_RELEASE;
  9734			ret = check_func_arg_reg_off(env, reg, regno, arg_type);
  9735			if (ret < 0)
  9736				return ret;
  9737	
  9738			switch (kf_arg_type) {
  9739			case KF_ARG_PTR_TO_CTX:
  9740				if (reg->type != PTR_TO_CTX) {
  9741					verbose(env, "arg#%d expected pointer to ctx, but got %s\n", i, btf_type_str(t));
  9742					return -EINVAL;
  9743				}
  9744	
  9745				if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) {
  9746					ret = get_kern_ctx_btf_id(&env->log, resolve_prog_type(env->prog));
  9747					if (ret < 0)
  9748						return -EINVAL;
  9749					meta->ret_btf_id  = ret;
  9750				}
  9751				break;
  9752			case KF_ARG_PTR_TO_ALLOC_BTF_ID:
  9753				if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
  9754					verbose(env, "arg#%d expected pointer to allocated object\n", i);
  9755					return -EINVAL;
  9756				}
  9757				if (!reg->ref_obj_id) {
  9758					verbose(env, "allocated object must be referenced\n");
  9759					return -EINVAL;
  9760				}
  9761				if (meta->btf == btf_vmlinux &&
  9762				    meta->func_id == special_kfunc_list[KF_bpf_obj_drop_impl]) {
  9763					meta->arg_obj_drop.btf = reg->btf;
  9764					meta->arg_obj_drop.btf_id = reg->btf_id;
  9765				}
  9766				break;
  9767			case KF_ARG_PTR_TO_KPTR:
  9768				if (reg->type != PTR_TO_MAP_VALUE) {
  9769					verbose(env, "arg#0 expected pointer to map value\n");
  9770					return -EINVAL;
  9771				}
  9772				ret = process_kf_arg_ptr_to_kptr(env, reg, ref_t, ref_tname, meta, i);
  9773				if (ret < 0)
  9774					return ret;
  9775				break;
  9776			case KF_ARG_PTR_TO_DYNPTR:
  9777			{
  9778				enum bpf_arg_type dynptr_arg_type = ARG_PTR_TO_DYNPTR;
  9779	
  9780				if (reg->type != PTR_TO_STACK &&
  9781				    reg->type != CONST_PTR_TO_DYNPTR) {
  9782					verbose(env, "arg#%d expected pointer to stack or dynptr_ptr\n", i);
  9783					return -EINVAL;
  9784				}
  9785	
  9786				if (reg->type == CONST_PTR_TO_DYNPTR)
  9787					dynptr_arg_type |= MEM_RDONLY;
  9788	
  9789				if (is_kfunc_arg_uninit(btf, &args[i]))
  9790					dynptr_arg_type |= MEM_UNINIT;
  9791	
  9792				if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_skb])
  9793					dynptr_arg_type |= DYNPTR_TYPE_SKB;
  9794				else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_xdp])
  9795					dynptr_arg_type |= DYNPTR_TYPE_XDP;
  9796	
  9797				ret = process_dynptr_func(env, regno, insn_idx, dynptr_arg_type);
  9798				if (ret < 0)
  9799					return ret;
  9800	
  9801				if (!(dynptr_arg_type & MEM_UNINIT)) {
  9802					int id = dynptr_id(env, reg);
  9803	
  9804					if (id < 0) {
  9805						verbose(env, "verifier internal error: failed to obtain dynptr id\n");
  9806						return id;
  9807					}
  9808					meta->initialized_dynptr.id = id;
  9809					meta->initialized_dynptr.type = dynptr_get_type(env, reg);
  9810				}
  9811	
  9812				break;
  9813			}
  9814			case KF_ARG_PTR_TO_LIST_HEAD:
  9815				if (reg->type != PTR_TO_MAP_VALUE &&
  9816				    reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
  9817					verbose(env, "arg#%d expected pointer to map value or allocated object\n", i);
  9818					return -EINVAL;
  9819				}
  9820				if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) && !reg->ref_obj_id) {
  9821					verbose(env, "allocated object must be referenced\n");
  9822					return -EINVAL;
  9823				}
  9824				ret = process_kf_arg_ptr_to_list_head(env, reg, regno, meta);
  9825				if (ret < 0)
  9826					return ret;
  9827				break;
  9828			case KF_ARG_PTR_TO_RB_ROOT:
  9829				if (reg->type != PTR_TO_MAP_VALUE &&
  9830				    reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
  9831					verbose(env, "arg#%d expected pointer to map value or allocated object\n", i);
  9832					return -EINVAL;
  9833				}
  9834				if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) && !reg->ref_obj_id) {
  9835					verbose(env, "allocated object must be referenced\n");
  9836					return -EINVAL;
  9837				}
  9838				ret = process_kf_arg_ptr_to_rbtree_root(env, reg, regno, meta);
  9839				if (ret < 0)
  9840					return ret;
  9841				break;
  9842			case KF_ARG_PTR_TO_LIST_NODE:
  9843				if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
  9844					verbose(env, "arg#%d expected pointer to allocated object\n", i);
  9845					return -EINVAL;
  9846				}
  9847				if (!reg->ref_obj_id) {
  9848					verbose(env, "allocated object must be referenced\n");
  9849					return -EINVAL;
  9850				}
  9851				ret = process_kf_arg_ptr_to_list_node(env, reg, regno, meta);
  9852				if (ret < 0)
  9853					return ret;
  9854				break;
  9855			case KF_ARG_PTR_TO_RB_NODE:
  9856				if (meta->func_id == special_kfunc_list[KF_bpf_rbtree_remove]) {
  9857					if (!type_is_non_owning_ref(reg->type) || reg->ref_obj_id) {
  9858						verbose(env, "rbtree_remove node input must be non-owning ref\n");
  9859						return -EINVAL;
  9860					}
  9861					if (in_rbtree_lock_required_cb(env)) {
  9862						verbose(env, "rbtree_remove not allowed in rbtree cb\n");
  9863						return -EINVAL;
  9864					}
  9865				} else {
  9866					if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
  9867						verbose(env, "arg#%d expected pointer to allocated object\n", i);
  9868						return -EINVAL;
  9869					}
  9870					if (!reg->ref_obj_id) {
  9871						verbose(env, "allocated object must be referenced\n");
  9872						return -EINVAL;
  9873					}
  9874				}
  9875	
  9876				ret = process_kf_arg_ptr_to_rbtree_node(env, reg, regno, meta);
  9877				if (ret < 0)
  9878					return ret;
  9879				break;
  9880			case KF_ARG_PTR_TO_BTF_ID:
  9881				/* Only base_type is checked, further checks are done here */
  9882				if ((base_type(reg->type) != PTR_TO_BTF_ID ||
  9883				     (bpf_type_has_unsafe_modifiers(reg->type) && !is_rcu_reg(reg))) &&
  9884				    !reg2btf_ids[base_type(reg->type)]) {
  9885					verbose(env, "arg#%d is %s ", i, reg_type_str(env, reg->type));
  9886					verbose(env, "expected %s or socket\n",
  9887						reg_type_str(env, base_type(reg->type) |
  9888								  (type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS)));
  9889					return -EINVAL;
  9890				}
  9891				ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname, ref_id, meta, i);
  9892				if (ret < 0)
  9893					return ret;
  9894				break;
  9895			case KF_ARG_PTR_TO_MEM:
  9896				resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
  9897				if (IS_ERR(resolve_ret)) {
  9898					verbose(env, "arg#%d reference type('%s %s') size cannot be determined: %ld\n",
  9899						i, btf_type_str(ref_t), ref_tname, PTR_ERR(resolve_ret));
  9900					return -EINVAL;
  9901				}
  9902				ret = check_mem_reg(env, reg, regno, type_size);
  9903				if (ret < 0)
  9904					return ret;
  9905				break;
  9906			case KF_ARG_PTR_TO_MEM_SIZE:
  9907				struct bpf_reg_state *size_reg = &regs[regno + 1];
> 9908				const struct btf_param *size_arg = &args[i + 1];
  9909	
  9910				ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1);
  9911				if (ret < 0) {
  9912					verbose(env, "arg#%d arg#%d memory, len pair leads to invalid memory access\n", i, i + 1);
  9913					return ret;
  9914				}
  9915	
  9916				if (is_kfunc_arg_const_mem_size(meta->btf, size_arg, size_reg)) {
  9917					if (meta->arg_constant.found) {
  9918						verbose(env, "verifier internal error: only one constant argument permitted\n");
  9919						return -EFAULT;
  9920					}
  9921					if (!tnum_is_const(size_reg->var_off)) {
  9922						verbose(env, "R%d must be a known constant\n", regno + 1);
  9923						return -EINVAL;
  9924					}
  9925					meta->arg_constant.found = true;
  9926					meta->arg_constant.value = size_reg->var_off.value;
  9927				}
  9928	
  9929				/* Skip next '__sz' or '__szk' argument */
  9930				i++;
  9931				break;
  9932			case KF_ARG_PTR_TO_CALLBACK:
  9933				meta->subprogno = reg->subprogno;
  9934				break;
  9935			}
  9936		}
  9937	
  9938		if (is_kfunc_release(meta) && !meta->release_regno) {
  9939			verbose(env, "release kernel function %s expects refcounted PTR_TO_BTF_ID\n",
  9940				func_name);
  9941			return -EINVAL;
  9942		}
  9943	
  9944		return 0;
  9945	}
  9946
kernel test robot Feb. 26, 2023, 10:14 a.m. UTC | #2
Hi Joanne,

Thank you for the patch! Perhaps something to improve:

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

url:    https://github.com/intel-lab-lkp/linux/commits/Joanne-Koong/bpf-Support-sk_buff-and-xdp_buff-as-valid-kfunc-arg-types/20230226-165406
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
patch link:    https://lore.kernel.org/r/20230226085120.3907863-10-joannelkoong%40gmail.com
patch subject: [PATCH v12 bpf-next 09/10] bpf: Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr
config: i386-randconfig-a002 (https://download.01.org/0day-ci/archive/20230226/202302261844.BdMQvIkw-lkp@intel.com/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/bc5a61c43c72539ef11e6435a168bf240a186ac1
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Joanne-Koong/bpf-Support-sk_buff-and-xdp_buff-as-valid-kfunc-arg-types/20230226-165406
        git checkout bc5a61c43c72539ef11e6435a168bf240a186ac1
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash kernel/bpf/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202302261844.BdMQvIkw-lkp@intel.com/

All warnings (new ones prefixed by >>):

   kernel/bpf/verifier.c:6253:5: warning: no previous prototype for function 'process_dynptr_func' [-Wmissing-prototypes]
   int process_dynptr_func(struct bpf_verifier_env *env, int regno, int insn_idx,
       ^
   kernel/bpf/verifier.c:6253:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   int process_dynptr_func(struct bpf_verifier_env *env, int regno, int insn_idx,
   ^
   static 
   kernel/bpf/verifier.c:9907:4: error: expected expression
                           struct bpf_reg_state *size_reg = &regs[regno + 1];
                           ^
   kernel/bpf/verifier.c:9910:40: error: use of undeclared identifier 'size_reg'; did you mean 'size_arg'?
                           ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1);
                                                               ^~~~~~~~
                                                               size_arg
   kernel/bpf/verifier.c:9908:28: note: 'size_arg' declared here
                           const struct btf_param *size_arg = &args[i + 1];
                                                   ^
   kernel/bpf/verifier.c:9916:57: error: use of undeclared identifier 'size_reg'; did you mean 'size_arg'?
                           if (is_kfunc_arg_const_mem_size(meta->btf, size_arg, size_reg)) {
                                                                                ^~~~~~~~
                                                                                size_arg
   kernel/bpf/verifier.c:9908:28: note: 'size_arg' declared here
                           const struct btf_param *size_arg = &args[i + 1];
                                                   ^
   kernel/bpf/verifier.c:9921:24: error: use of undeclared identifier 'size_reg'
                                   if (!tnum_is_const(size_reg->var_off)) {
                                                      ^
   kernel/bpf/verifier.c:9926:32: error: use of undeclared identifier 'size_reg'
                                   meta->arg_constant.value = size_reg->var_off.value;
                                                              ^
>> kernel/bpf/verifier.c:9908:28: warning: mixing declarations and code is incompatible with standards before C99 [-Wdeclaration-after-statement]
                           const struct btf_param *size_arg = &args[i + 1];
                                                   ^
   kernel/bpf/verifier.c:10167:24: warning: array index 16 is past the end of the array (which contains 16 elements) [-Warray-bounds]
                                      meta.func_id == special_kfunc_list[KF_bpf_dynptr_slice_rdwr]) {
                                                      ^                  ~~~~~~~~~~~~~~~~~~~~~~~~
   kernel/bpf/verifier.c:9017:1: note: array 'special_kfunc_list' declared here
   BTF_ID_LIST(special_kfunc_list)
   ^
   include/linux/btf_ids.h:207:27: note: expanded from macro 'BTF_ID_LIST'
   #define BTF_ID_LIST(name) static u32 __maybe_unused name[16];
                             ^
   3 warnings and 5 errors generated.


vim +9908 kernel/bpf/verifier.c

  9584	
  9585	static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_arg_meta *meta,
  9586				    int insn_idx)
  9587	{
  9588		const char *func_name = meta->func_name, *ref_tname;
  9589		const struct btf *btf = meta->btf;
  9590		const struct btf_param *args;
  9591		u32 i, nargs;
  9592		int ret;
  9593	
  9594		args = (const struct btf_param *)(meta->func_proto + 1);
  9595		nargs = btf_type_vlen(meta->func_proto);
  9596		if (nargs > MAX_BPF_FUNC_REG_ARGS) {
  9597			verbose(env, "Function %s has %d > %d args\n", func_name, nargs,
  9598				MAX_BPF_FUNC_REG_ARGS);
  9599			return -EINVAL;
  9600		}
  9601	
  9602		/* Check that BTF function arguments match actual types that the
  9603		 * verifier sees.
  9604		 */
  9605		for (i = 0; i < nargs; i++) {
  9606			struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[i + 1];
  9607			const struct btf_type *t, *ref_t, *resolve_ret;
  9608			enum bpf_arg_type arg_type = ARG_DONTCARE;
  9609			u32 regno = i + 1, ref_id, type_size;
  9610			bool is_ret_buf_sz = false;
  9611			int kf_arg_type;
  9612	
  9613			t = btf_type_skip_modifiers(btf, args[i].type, NULL);
  9614	
  9615			if (is_kfunc_arg_ignore(btf, &args[i]))
  9616				continue;
  9617	
  9618			if (btf_type_is_scalar(t)) {
  9619				if (reg->type != SCALAR_VALUE) {
  9620					verbose(env, "R%d is not a scalar\n", regno);
  9621					return -EINVAL;
  9622				}
  9623	
  9624				if (is_kfunc_arg_constant(meta->btf, &args[i])) {
  9625					if (meta->arg_constant.found) {
  9626						verbose(env, "verifier internal error: only one constant argument permitted\n");
  9627						return -EFAULT;
  9628					}
  9629					if (!tnum_is_const(reg->var_off)) {
  9630						verbose(env, "R%d must be a known constant\n", regno);
  9631						return -EINVAL;
  9632					}
  9633					ret = mark_chain_precision(env, regno);
  9634					if (ret < 0)
  9635						return ret;
  9636					meta->arg_constant.found = true;
  9637					meta->arg_constant.value = reg->var_off.value;
  9638				} else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size")) {
  9639					meta->r0_rdonly = true;
  9640					is_ret_buf_sz = true;
  9641				} else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdwr_buf_size")) {
  9642					is_ret_buf_sz = true;
  9643				}
  9644	
  9645				if (is_ret_buf_sz) {
  9646					if (meta->r0_size) {
  9647						verbose(env, "2 or more rdonly/rdwr_buf_size parameters for kfunc");
  9648						return -EINVAL;
  9649					}
  9650	
  9651					if (!tnum_is_const(reg->var_off)) {
  9652						verbose(env, "R%d is not a const\n", regno);
  9653						return -EINVAL;
  9654					}
  9655	
  9656					meta->r0_size = reg->var_off.value;
  9657					ret = mark_chain_precision(env, regno);
  9658					if (ret)
  9659						return ret;
  9660				}
  9661				continue;
  9662			}
  9663	
  9664			if (!btf_type_is_ptr(t)) {
  9665				verbose(env, "Unrecognized arg#%d type %s\n", i, btf_type_str(t));
  9666				return -EINVAL;
  9667			}
  9668	
  9669			if (is_kfunc_trusted_args(meta) &&
  9670			    (register_is_null(reg) || type_may_be_null(reg->type))) {
  9671				verbose(env, "Possibly NULL pointer passed to trusted arg%d\n", i);
  9672				return -EACCES;
  9673			}
  9674	
  9675			if (reg->ref_obj_id) {
  9676				if (is_kfunc_release(meta) && meta->ref_obj_id) {
  9677					verbose(env, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n",
  9678						regno, reg->ref_obj_id,
  9679						meta->ref_obj_id);
  9680					return -EFAULT;
  9681				}
  9682				meta->ref_obj_id = reg->ref_obj_id;
  9683				if (is_kfunc_release(meta))
  9684					meta->release_regno = regno;
  9685			}
  9686	
  9687			ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
  9688			ref_tname = btf_name_by_offset(btf, ref_t->name_off);
  9689	
  9690			kf_arg_type = get_kfunc_ptr_arg_type(env, meta, t, ref_t, ref_tname, args, i, nargs);
  9691			if (kf_arg_type < 0)
  9692				return kf_arg_type;
  9693	
  9694			switch (kf_arg_type) {
  9695			case KF_ARG_PTR_TO_ALLOC_BTF_ID:
  9696			case KF_ARG_PTR_TO_BTF_ID:
  9697				if (!is_kfunc_trusted_args(meta) && !is_kfunc_rcu(meta))
  9698					break;
  9699	
  9700				if (!is_trusted_reg(reg)) {
  9701					if (!is_kfunc_rcu(meta)) {
  9702						verbose(env, "R%d must be referenced or trusted\n", regno);
  9703						return -EINVAL;
  9704					}
  9705					if (!is_rcu_reg(reg)) {
  9706						verbose(env, "R%d must be a rcu pointer\n", regno);
  9707						return -EINVAL;
  9708					}
  9709				}
  9710	
  9711				fallthrough;
  9712			case KF_ARG_PTR_TO_CTX:
  9713				/* Trusted arguments have the same offset checks as release arguments */
  9714				arg_type |= OBJ_RELEASE;
  9715				break;
  9716			case KF_ARG_PTR_TO_KPTR:
  9717			case KF_ARG_PTR_TO_DYNPTR:
  9718			case KF_ARG_PTR_TO_LIST_HEAD:
  9719			case KF_ARG_PTR_TO_LIST_NODE:
  9720			case KF_ARG_PTR_TO_RB_ROOT:
  9721			case KF_ARG_PTR_TO_RB_NODE:
  9722			case KF_ARG_PTR_TO_MEM:
  9723			case KF_ARG_PTR_TO_MEM_SIZE:
  9724			case KF_ARG_PTR_TO_CALLBACK:
  9725				/* Trusted by default */
  9726				break;
  9727			default:
  9728				WARN_ON_ONCE(1);
  9729				return -EFAULT;
  9730			}
  9731	
  9732			if (is_kfunc_release(meta) && reg->ref_obj_id)
  9733				arg_type |= OBJ_RELEASE;
  9734			ret = check_func_arg_reg_off(env, reg, regno, arg_type);
  9735			if (ret < 0)
  9736				return ret;
  9737	
  9738			switch (kf_arg_type) {
  9739			case KF_ARG_PTR_TO_CTX:
  9740				if (reg->type != PTR_TO_CTX) {
  9741					verbose(env, "arg#%d expected pointer to ctx, but got %s\n", i, btf_type_str(t));
  9742					return -EINVAL;
  9743				}
  9744	
  9745				if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) {
  9746					ret = get_kern_ctx_btf_id(&env->log, resolve_prog_type(env->prog));
  9747					if (ret < 0)
  9748						return -EINVAL;
  9749					meta->ret_btf_id  = ret;
  9750				}
  9751				break;
  9752			case KF_ARG_PTR_TO_ALLOC_BTF_ID:
  9753				if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
  9754					verbose(env, "arg#%d expected pointer to allocated object\n", i);
  9755					return -EINVAL;
  9756				}
  9757				if (!reg->ref_obj_id) {
  9758					verbose(env, "allocated object must be referenced\n");
  9759					return -EINVAL;
  9760				}
  9761				if (meta->btf == btf_vmlinux &&
  9762				    meta->func_id == special_kfunc_list[KF_bpf_obj_drop_impl]) {
  9763					meta->arg_obj_drop.btf = reg->btf;
  9764					meta->arg_obj_drop.btf_id = reg->btf_id;
  9765				}
  9766				break;
  9767			case KF_ARG_PTR_TO_KPTR:
  9768				if (reg->type != PTR_TO_MAP_VALUE) {
  9769					verbose(env, "arg#0 expected pointer to map value\n");
  9770					return -EINVAL;
  9771				}
  9772				ret = process_kf_arg_ptr_to_kptr(env, reg, ref_t, ref_tname, meta, i);
  9773				if (ret < 0)
  9774					return ret;
  9775				break;
  9776			case KF_ARG_PTR_TO_DYNPTR:
  9777			{
  9778				enum bpf_arg_type dynptr_arg_type = ARG_PTR_TO_DYNPTR;
  9779	
  9780				if (reg->type != PTR_TO_STACK &&
  9781				    reg->type != CONST_PTR_TO_DYNPTR) {
  9782					verbose(env, "arg#%d expected pointer to stack or dynptr_ptr\n", i);
  9783					return -EINVAL;
  9784				}
  9785	
  9786				if (reg->type == CONST_PTR_TO_DYNPTR)
  9787					dynptr_arg_type |= MEM_RDONLY;
  9788	
  9789				if (is_kfunc_arg_uninit(btf, &args[i]))
  9790					dynptr_arg_type |= MEM_UNINIT;
  9791	
  9792				if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_skb])
  9793					dynptr_arg_type |= DYNPTR_TYPE_SKB;
  9794				else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_xdp])
  9795					dynptr_arg_type |= DYNPTR_TYPE_XDP;
  9796	
  9797				ret = process_dynptr_func(env, regno, insn_idx, dynptr_arg_type);
  9798				if (ret < 0)
  9799					return ret;
  9800	
  9801				if (!(dynptr_arg_type & MEM_UNINIT)) {
  9802					int id = dynptr_id(env, reg);
  9803	
  9804					if (id < 0) {
  9805						verbose(env, "verifier internal error: failed to obtain dynptr id\n");
  9806						return id;
  9807					}
  9808					meta->initialized_dynptr.id = id;
  9809					meta->initialized_dynptr.type = dynptr_get_type(env, reg);
  9810				}
  9811	
  9812				break;
  9813			}
  9814			case KF_ARG_PTR_TO_LIST_HEAD:
  9815				if (reg->type != PTR_TO_MAP_VALUE &&
  9816				    reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
  9817					verbose(env, "arg#%d expected pointer to map value or allocated object\n", i);
  9818					return -EINVAL;
  9819				}
  9820				if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) && !reg->ref_obj_id) {
  9821					verbose(env, "allocated object must be referenced\n");
  9822					return -EINVAL;
  9823				}
  9824				ret = process_kf_arg_ptr_to_list_head(env, reg, regno, meta);
  9825				if (ret < 0)
  9826					return ret;
  9827				break;
  9828			case KF_ARG_PTR_TO_RB_ROOT:
  9829				if (reg->type != PTR_TO_MAP_VALUE &&
  9830				    reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
  9831					verbose(env, "arg#%d expected pointer to map value or allocated object\n", i);
  9832					return -EINVAL;
  9833				}
  9834				if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) && !reg->ref_obj_id) {
  9835					verbose(env, "allocated object must be referenced\n");
  9836					return -EINVAL;
  9837				}
  9838				ret = process_kf_arg_ptr_to_rbtree_root(env, reg, regno, meta);
  9839				if (ret < 0)
  9840					return ret;
  9841				break;
  9842			case KF_ARG_PTR_TO_LIST_NODE:
  9843				if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
  9844					verbose(env, "arg#%d expected pointer to allocated object\n", i);
  9845					return -EINVAL;
  9846				}
  9847				if (!reg->ref_obj_id) {
  9848					verbose(env, "allocated object must be referenced\n");
  9849					return -EINVAL;
  9850				}
  9851				ret = process_kf_arg_ptr_to_list_node(env, reg, regno, meta);
  9852				if (ret < 0)
  9853					return ret;
  9854				break;
  9855			case KF_ARG_PTR_TO_RB_NODE:
  9856				if (meta->func_id == special_kfunc_list[KF_bpf_rbtree_remove]) {
  9857					if (!type_is_non_owning_ref(reg->type) || reg->ref_obj_id) {
  9858						verbose(env, "rbtree_remove node input must be non-owning ref\n");
  9859						return -EINVAL;
  9860					}
  9861					if (in_rbtree_lock_required_cb(env)) {
  9862						verbose(env, "rbtree_remove not allowed in rbtree cb\n");
  9863						return -EINVAL;
  9864					}
  9865				} else {
  9866					if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
  9867						verbose(env, "arg#%d expected pointer to allocated object\n", i);
  9868						return -EINVAL;
  9869					}
  9870					if (!reg->ref_obj_id) {
  9871						verbose(env, "allocated object must be referenced\n");
  9872						return -EINVAL;
  9873					}
  9874				}
  9875	
  9876				ret = process_kf_arg_ptr_to_rbtree_node(env, reg, regno, meta);
  9877				if (ret < 0)
  9878					return ret;
  9879				break;
  9880			case KF_ARG_PTR_TO_BTF_ID:
  9881				/* Only base_type is checked, further checks are done here */
  9882				if ((base_type(reg->type) != PTR_TO_BTF_ID ||
  9883				     (bpf_type_has_unsafe_modifiers(reg->type) && !is_rcu_reg(reg))) &&
  9884				    !reg2btf_ids[base_type(reg->type)]) {
  9885					verbose(env, "arg#%d is %s ", i, reg_type_str(env, reg->type));
  9886					verbose(env, "expected %s or socket\n",
  9887						reg_type_str(env, base_type(reg->type) |
  9888								  (type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS)));
  9889					return -EINVAL;
  9890				}
  9891				ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname, ref_id, meta, i);
  9892				if (ret < 0)
  9893					return ret;
  9894				break;
  9895			case KF_ARG_PTR_TO_MEM:
  9896				resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
  9897				if (IS_ERR(resolve_ret)) {
  9898					verbose(env, "arg#%d reference type('%s %s') size cannot be determined: %ld\n",
  9899						i, btf_type_str(ref_t), ref_tname, PTR_ERR(resolve_ret));
  9900					return -EINVAL;
  9901				}
  9902				ret = check_mem_reg(env, reg, regno, type_size);
  9903				if (ret < 0)
  9904					return ret;
  9905				break;
  9906			case KF_ARG_PTR_TO_MEM_SIZE:
  9907				struct bpf_reg_state *size_reg = &regs[regno + 1];
> 9908				const struct btf_param *size_arg = &args[i + 1];
  9909	
  9910				ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1);
  9911				if (ret < 0) {
  9912					verbose(env, "arg#%d arg#%d memory, len pair leads to invalid memory access\n", i, i + 1);
  9913					return ret;
  9914				}
  9915	
  9916				if (is_kfunc_arg_const_mem_size(meta->btf, size_arg, size_reg)) {
  9917					if (meta->arg_constant.found) {
  9918						verbose(env, "verifier internal error: only one constant argument permitted\n");
  9919						return -EFAULT;
  9920					}
  9921					if (!tnum_is_const(size_reg->var_off)) {
  9922						verbose(env, "R%d must be a known constant\n", regno + 1);
  9923						return -EINVAL;
  9924					}
  9925					meta->arg_constant.found = true;
  9926					meta->arg_constant.value = size_reg->var_off.value;
  9927				}
  9928	
  9929				/* Skip next '__sz' or '__szk' argument */
  9930				i++;
  9931				break;
  9932			case KF_ARG_PTR_TO_CALLBACK:
  9933				meta->subprogno = reg->subprogno;
  9934				break;
  9935			}
  9936		}
  9937	
  9938		if (is_kfunc_release(meta) && !meta->release_regno) {
  9939			verbose(env, "release kernel function %s expects refcounted PTR_TO_BTF_ID\n",
  9940				func_name);
  9941			return -EINVAL;
  9942		}
  9943	
  9944		return 0;
  9945	}
  9946
kernel test robot Feb. 26, 2023, 10:54 a.m. UTC | #3
Hi Joanne,

Thank you for the patch! Yet something to improve:

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

url:    https://github.com/intel-lab-lkp/linux/commits/Joanne-Koong/bpf-Support-sk_buff-and-xdp_buff-as-valid-kfunc-arg-types/20230226-165406
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next.git master
patch link:    https://lore.kernel.org/r/20230226085120.3907863-10-joannelkoong%40gmail.com
patch subject: [PATCH v12 bpf-next 09/10] bpf: Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr
config: hexagon-randconfig-r026-20230226 (https://download.01.org/0day-ci/archive/20230226/202302261821.69VtQP9g-lkp@intel.com/config)
compiler: clang version 17.0.0 (https://github.com/llvm/llvm-project db89896bbbd2251fff457699635acbbedeead27f)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/bc5a61c43c72539ef11e6435a168bf240a186ac1
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Joanne-Koong/bpf-Support-sk_buff-and-xdp_buff-as-valid-kfunc-arg-types/20230226-165406
        git checkout bc5a61c43c72539ef11e6435a168bf240a186ac1
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/202302261821.69VtQP9g-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from kernel/bpf/verifier.c:7:
   In file included from include/linux/bpf-cgroup.h:5:
   In file included from include/linux/bpf.h:31:
   In file included from include/linux/memcontrol.h:13:
   In file included from include/linux/cgroup.h:26:
   In file included from include/linux/kernel_stat.h:9:
   In file included from include/linux/interrupt.h:11:
   In file included from include/linux/hardirq.h:11:
   In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1:
   In file included from include/asm-generic/hardirq.h:17:
   In file included from include/linux/irq.h:20:
   In file included from include/linux/io.h:13:
   In file included from arch/hexagon/include/asm/io.h:334:
   include/asm-generic/io.h:547:31: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           val = __raw_readb(PCI_IOBASE + addr);
                             ~~~~~~~~~~ ^
   include/asm-generic/io.h:560:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           val = __le16_to_cpu((__le16 __force)__raw_readw(PCI_IOBASE + addr));
                                                           ~~~~~~~~~~ ^
   include/uapi/linux/byteorder/little_endian.h:37:51: note: expanded from macro '__le16_to_cpu'
   #define __le16_to_cpu(x) ((__force __u16)(__le16)(x))
                                                     ^
   In file included from kernel/bpf/verifier.c:7:
   In file included from include/linux/bpf-cgroup.h:5:
   In file included from include/linux/bpf.h:31:
   In file included from include/linux/memcontrol.h:13:
   In file included from include/linux/cgroup.h:26:
   In file included from include/linux/kernel_stat.h:9:
   In file included from include/linux/interrupt.h:11:
   In file included from include/linux/hardirq.h:11:
   In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1:
   In file included from include/asm-generic/hardirq.h:17:
   In file included from include/linux/irq.h:20:
   In file included from include/linux/io.h:13:
   In file included from arch/hexagon/include/asm/io.h:334:
   include/asm-generic/io.h:573:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           val = __le32_to_cpu((__le32 __force)__raw_readl(PCI_IOBASE + addr));
                                                           ~~~~~~~~~~ ^
   include/uapi/linux/byteorder/little_endian.h:35:51: note: expanded from macro '__le32_to_cpu'
   #define __le32_to_cpu(x) ((__force __u32)(__le32)(x))
                                                     ^
   In file included from kernel/bpf/verifier.c:7:
   In file included from include/linux/bpf-cgroup.h:5:
   In file included from include/linux/bpf.h:31:
   In file included from include/linux/memcontrol.h:13:
   In file included from include/linux/cgroup.h:26:
   In file included from include/linux/kernel_stat.h:9:
   In file included from include/linux/interrupt.h:11:
   In file included from include/linux/hardirq.h:11:
   In file included from ./arch/hexagon/include/generated/asm/hardirq.h:1:
   In file included from include/asm-generic/hardirq.h:17:
   In file included from include/linux/irq.h:20:
   In file included from include/linux/io.h:13:
   In file included from arch/hexagon/include/asm/io.h:334:
   include/asm-generic/io.h:584:33: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           __raw_writeb(value, PCI_IOBASE + addr);
                               ~~~~~~~~~~ ^
   include/asm-generic/io.h:594:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           __raw_writew((u16 __force)cpu_to_le16(value), PCI_IOBASE + addr);
                                                         ~~~~~~~~~~ ^
   include/asm-generic/io.h:604:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
           __raw_writel((u32 __force)cpu_to_le32(value), PCI_IOBASE + addr);
                                                         ~~~~~~~~~~ ^
   kernel/bpf/verifier.c:6253:5: warning: no previous prototype for function 'process_dynptr_func' [-Wmissing-prototypes]
   int process_dynptr_func(struct bpf_verifier_env *env, int regno, int insn_idx,
       ^
   kernel/bpf/verifier.c:6253:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   int process_dynptr_func(struct bpf_verifier_env *env, int regno, int insn_idx,
   ^
   static 
>> kernel/bpf/verifier.c:9907:4: error: expected expression
                           struct bpf_reg_state *size_reg = &regs[regno + 1];
                           ^
>> kernel/bpf/verifier.c:9910:40: error: use of undeclared identifier 'size_reg'; did you mean 'size_arg'?
                           ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1);
                                                               ^~~~~~~~
                                                               size_arg
   kernel/bpf/verifier.c:9908:28: note: 'size_arg' declared here
                           const struct btf_param *size_arg = &args[i + 1];
                                                   ^
   kernel/bpf/verifier.c:9916:57: error: use of undeclared identifier 'size_reg'; did you mean 'size_arg'?
                           if (is_kfunc_arg_const_mem_size(meta->btf, size_arg, size_reg)) {
                                                                                ^~~~~~~~
                                                                                size_arg
   kernel/bpf/verifier.c:9908:28: note: 'size_arg' declared here
                           const struct btf_param *size_arg = &args[i + 1];
                                                   ^
   kernel/bpf/verifier.c:9921:24: error: use of undeclared identifier 'size_reg'
                                   if (!tnum_is_const(size_reg->var_off)) {
                                                      ^
   kernel/bpf/verifier.c:9926:32: error: use of undeclared identifier 'size_reg'
                                   meta->arg_constant.value = size_reg->var_off.value;
                                                              ^
   kernel/bpf/verifier.c:9908:28: warning: mixing declarations and code is incompatible with standards before C99 [-Wdeclaration-after-statement]
                           const struct btf_param *size_arg = &args[i + 1];
                                                   ^
   8 warnings and 5 errors generated.


vim +9907 kernel/bpf/verifier.c

  9584	
  9585	static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_arg_meta *meta,
  9586				    int insn_idx)
  9587	{
  9588		const char *func_name = meta->func_name, *ref_tname;
  9589		const struct btf *btf = meta->btf;
  9590		const struct btf_param *args;
  9591		u32 i, nargs;
  9592		int ret;
  9593	
  9594		args = (const struct btf_param *)(meta->func_proto + 1);
  9595		nargs = btf_type_vlen(meta->func_proto);
  9596		if (nargs > MAX_BPF_FUNC_REG_ARGS) {
  9597			verbose(env, "Function %s has %d > %d args\n", func_name, nargs,
  9598				MAX_BPF_FUNC_REG_ARGS);
  9599			return -EINVAL;
  9600		}
  9601	
  9602		/* Check that BTF function arguments match actual types that the
  9603		 * verifier sees.
  9604		 */
  9605		for (i = 0; i < nargs; i++) {
  9606			struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[i + 1];
  9607			const struct btf_type *t, *ref_t, *resolve_ret;
  9608			enum bpf_arg_type arg_type = ARG_DONTCARE;
  9609			u32 regno = i + 1, ref_id, type_size;
  9610			bool is_ret_buf_sz = false;
  9611			int kf_arg_type;
  9612	
  9613			t = btf_type_skip_modifiers(btf, args[i].type, NULL);
  9614	
  9615			if (is_kfunc_arg_ignore(btf, &args[i]))
  9616				continue;
  9617	
  9618			if (btf_type_is_scalar(t)) {
  9619				if (reg->type != SCALAR_VALUE) {
  9620					verbose(env, "R%d is not a scalar\n", regno);
  9621					return -EINVAL;
  9622				}
  9623	
  9624				if (is_kfunc_arg_constant(meta->btf, &args[i])) {
  9625					if (meta->arg_constant.found) {
  9626						verbose(env, "verifier internal error: only one constant argument permitted\n");
  9627						return -EFAULT;
  9628					}
  9629					if (!tnum_is_const(reg->var_off)) {
  9630						verbose(env, "R%d must be a known constant\n", regno);
  9631						return -EINVAL;
  9632					}
  9633					ret = mark_chain_precision(env, regno);
  9634					if (ret < 0)
  9635						return ret;
  9636					meta->arg_constant.found = true;
  9637					meta->arg_constant.value = reg->var_off.value;
  9638				} else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdonly_buf_size")) {
  9639					meta->r0_rdonly = true;
  9640					is_ret_buf_sz = true;
  9641				} else if (is_kfunc_arg_scalar_with_name(btf, &args[i], "rdwr_buf_size")) {
  9642					is_ret_buf_sz = true;
  9643				}
  9644	
  9645				if (is_ret_buf_sz) {
  9646					if (meta->r0_size) {
  9647						verbose(env, "2 or more rdonly/rdwr_buf_size parameters for kfunc");
  9648						return -EINVAL;
  9649					}
  9650	
  9651					if (!tnum_is_const(reg->var_off)) {
  9652						verbose(env, "R%d is not a const\n", regno);
  9653						return -EINVAL;
  9654					}
  9655	
  9656					meta->r0_size = reg->var_off.value;
  9657					ret = mark_chain_precision(env, regno);
  9658					if (ret)
  9659						return ret;
  9660				}
  9661				continue;
  9662			}
  9663	
  9664			if (!btf_type_is_ptr(t)) {
  9665				verbose(env, "Unrecognized arg#%d type %s\n", i, btf_type_str(t));
  9666				return -EINVAL;
  9667			}
  9668	
  9669			if (is_kfunc_trusted_args(meta) &&
  9670			    (register_is_null(reg) || type_may_be_null(reg->type))) {
  9671				verbose(env, "Possibly NULL pointer passed to trusted arg%d\n", i);
  9672				return -EACCES;
  9673			}
  9674	
  9675			if (reg->ref_obj_id) {
  9676				if (is_kfunc_release(meta) && meta->ref_obj_id) {
  9677					verbose(env, "verifier internal error: more than one arg with ref_obj_id R%d %u %u\n",
  9678						regno, reg->ref_obj_id,
  9679						meta->ref_obj_id);
  9680					return -EFAULT;
  9681				}
  9682				meta->ref_obj_id = reg->ref_obj_id;
  9683				if (is_kfunc_release(meta))
  9684					meta->release_regno = regno;
  9685			}
  9686	
  9687			ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id);
  9688			ref_tname = btf_name_by_offset(btf, ref_t->name_off);
  9689	
  9690			kf_arg_type = get_kfunc_ptr_arg_type(env, meta, t, ref_t, ref_tname, args, i, nargs);
  9691			if (kf_arg_type < 0)
  9692				return kf_arg_type;
  9693	
  9694			switch (kf_arg_type) {
  9695			case KF_ARG_PTR_TO_ALLOC_BTF_ID:
  9696			case KF_ARG_PTR_TO_BTF_ID:
  9697				if (!is_kfunc_trusted_args(meta) && !is_kfunc_rcu(meta))
  9698					break;
  9699	
  9700				if (!is_trusted_reg(reg)) {
  9701					if (!is_kfunc_rcu(meta)) {
  9702						verbose(env, "R%d must be referenced or trusted\n", regno);
  9703						return -EINVAL;
  9704					}
  9705					if (!is_rcu_reg(reg)) {
  9706						verbose(env, "R%d must be a rcu pointer\n", regno);
  9707						return -EINVAL;
  9708					}
  9709				}
  9710	
  9711				fallthrough;
  9712			case KF_ARG_PTR_TO_CTX:
  9713				/* Trusted arguments have the same offset checks as release arguments */
  9714				arg_type |= OBJ_RELEASE;
  9715				break;
  9716			case KF_ARG_PTR_TO_KPTR:
  9717			case KF_ARG_PTR_TO_DYNPTR:
  9718			case KF_ARG_PTR_TO_LIST_HEAD:
  9719			case KF_ARG_PTR_TO_LIST_NODE:
  9720			case KF_ARG_PTR_TO_RB_ROOT:
  9721			case KF_ARG_PTR_TO_RB_NODE:
  9722			case KF_ARG_PTR_TO_MEM:
  9723			case KF_ARG_PTR_TO_MEM_SIZE:
  9724			case KF_ARG_PTR_TO_CALLBACK:
  9725				/* Trusted by default */
  9726				break;
  9727			default:
  9728				WARN_ON_ONCE(1);
  9729				return -EFAULT;
  9730			}
  9731	
  9732			if (is_kfunc_release(meta) && reg->ref_obj_id)
  9733				arg_type |= OBJ_RELEASE;
  9734			ret = check_func_arg_reg_off(env, reg, regno, arg_type);
  9735			if (ret < 0)
  9736				return ret;
  9737	
  9738			switch (kf_arg_type) {
  9739			case KF_ARG_PTR_TO_CTX:
  9740				if (reg->type != PTR_TO_CTX) {
  9741					verbose(env, "arg#%d expected pointer to ctx, but got %s\n", i, btf_type_str(t));
  9742					return -EINVAL;
  9743				}
  9744	
  9745				if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) {
  9746					ret = get_kern_ctx_btf_id(&env->log, resolve_prog_type(env->prog));
  9747					if (ret < 0)
  9748						return -EINVAL;
  9749					meta->ret_btf_id  = ret;
  9750				}
  9751				break;
  9752			case KF_ARG_PTR_TO_ALLOC_BTF_ID:
  9753				if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
  9754					verbose(env, "arg#%d expected pointer to allocated object\n", i);
  9755					return -EINVAL;
  9756				}
  9757				if (!reg->ref_obj_id) {
  9758					verbose(env, "allocated object must be referenced\n");
  9759					return -EINVAL;
  9760				}
  9761				if (meta->btf == btf_vmlinux &&
  9762				    meta->func_id == special_kfunc_list[KF_bpf_obj_drop_impl]) {
  9763					meta->arg_obj_drop.btf = reg->btf;
  9764					meta->arg_obj_drop.btf_id = reg->btf_id;
  9765				}
  9766				break;
  9767			case KF_ARG_PTR_TO_KPTR:
  9768				if (reg->type != PTR_TO_MAP_VALUE) {
  9769					verbose(env, "arg#0 expected pointer to map value\n");
  9770					return -EINVAL;
  9771				}
  9772				ret = process_kf_arg_ptr_to_kptr(env, reg, ref_t, ref_tname, meta, i);
  9773				if (ret < 0)
  9774					return ret;
  9775				break;
  9776			case KF_ARG_PTR_TO_DYNPTR:
  9777			{
  9778				enum bpf_arg_type dynptr_arg_type = ARG_PTR_TO_DYNPTR;
  9779	
  9780				if (reg->type != PTR_TO_STACK &&
  9781				    reg->type != CONST_PTR_TO_DYNPTR) {
  9782					verbose(env, "arg#%d expected pointer to stack or dynptr_ptr\n", i);
  9783					return -EINVAL;
  9784				}
  9785	
  9786				if (reg->type == CONST_PTR_TO_DYNPTR)
  9787					dynptr_arg_type |= MEM_RDONLY;
  9788	
  9789				if (is_kfunc_arg_uninit(btf, &args[i]))
  9790					dynptr_arg_type |= MEM_UNINIT;
  9791	
  9792				if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_skb])
  9793					dynptr_arg_type |= DYNPTR_TYPE_SKB;
  9794				else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_xdp])
  9795					dynptr_arg_type |= DYNPTR_TYPE_XDP;
  9796	
  9797				ret = process_dynptr_func(env, regno, insn_idx, dynptr_arg_type);
  9798				if (ret < 0)
  9799					return ret;
  9800	
  9801				if (!(dynptr_arg_type & MEM_UNINIT)) {
  9802					int id = dynptr_id(env, reg);
  9803	
  9804					if (id < 0) {
  9805						verbose(env, "verifier internal error: failed to obtain dynptr id\n");
  9806						return id;
  9807					}
  9808					meta->initialized_dynptr.id = id;
  9809					meta->initialized_dynptr.type = dynptr_get_type(env, reg);
  9810				}
  9811	
  9812				break;
  9813			}
  9814			case KF_ARG_PTR_TO_LIST_HEAD:
  9815				if (reg->type != PTR_TO_MAP_VALUE &&
  9816				    reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
  9817					verbose(env, "arg#%d expected pointer to map value or allocated object\n", i);
  9818					return -EINVAL;
  9819				}
  9820				if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) && !reg->ref_obj_id) {
  9821					verbose(env, "allocated object must be referenced\n");
  9822					return -EINVAL;
  9823				}
  9824				ret = process_kf_arg_ptr_to_list_head(env, reg, regno, meta);
  9825				if (ret < 0)
  9826					return ret;
  9827				break;
  9828			case KF_ARG_PTR_TO_RB_ROOT:
  9829				if (reg->type != PTR_TO_MAP_VALUE &&
  9830				    reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
  9831					verbose(env, "arg#%d expected pointer to map value or allocated object\n", i);
  9832					return -EINVAL;
  9833				}
  9834				if (reg->type == (PTR_TO_BTF_ID | MEM_ALLOC) && !reg->ref_obj_id) {
  9835					verbose(env, "allocated object must be referenced\n");
  9836					return -EINVAL;
  9837				}
  9838				ret = process_kf_arg_ptr_to_rbtree_root(env, reg, regno, meta);
  9839				if (ret < 0)
  9840					return ret;
  9841				break;
  9842			case KF_ARG_PTR_TO_LIST_NODE:
  9843				if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
  9844					verbose(env, "arg#%d expected pointer to allocated object\n", i);
  9845					return -EINVAL;
  9846				}
  9847				if (!reg->ref_obj_id) {
  9848					verbose(env, "allocated object must be referenced\n");
  9849					return -EINVAL;
  9850				}
  9851				ret = process_kf_arg_ptr_to_list_node(env, reg, regno, meta);
  9852				if (ret < 0)
  9853					return ret;
  9854				break;
  9855			case KF_ARG_PTR_TO_RB_NODE:
  9856				if (meta->func_id == special_kfunc_list[KF_bpf_rbtree_remove]) {
  9857					if (!type_is_non_owning_ref(reg->type) || reg->ref_obj_id) {
  9858						verbose(env, "rbtree_remove node input must be non-owning ref\n");
  9859						return -EINVAL;
  9860					}
  9861					if (in_rbtree_lock_required_cb(env)) {
  9862						verbose(env, "rbtree_remove not allowed in rbtree cb\n");
  9863						return -EINVAL;
  9864					}
  9865				} else {
  9866					if (reg->type != (PTR_TO_BTF_ID | MEM_ALLOC)) {
  9867						verbose(env, "arg#%d expected pointer to allocated object\n", i);
  9868						return -EINVAL;
  9869					}
  9870					if (!reg->ref_obj_id) {
  9871						verbose(env, "allocated object must be referenced\n");
  9872						return -EINVAL;
  9873					}
  9874				}
  9875	
  9876				ret = process_kf_arg_ptr_to_rbtree_node(env, reg, regno, meta);
  9877				if (ret < 0)
  9878					return ret;
  9879				break;
  9880			case KF_ARG_PTR_TO_BTF_ID:
  9881				/* Only base_type is checked, further checks are done here */
  9882				if ((base_type(reg->type) != PTR_TO_BTF_ID ||
  9883				     (bpf_type_has_unsafe_modifiers(reg->type) && !is_rcu_reg(reg))) &&
  9884				    !reg2btf_ids[base_type(reg->type)]) {
  9885					verbose(env, "arg#%d is %s ", i, reg_type_str(env, reg->type));
  9886					verbose(env, "expected %s or socket\n",
  9887						reg_type_str(env, base_type(reg->type) |
  9888								  (type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS)));
  9889					return -EINVAL;
  9890				}
  9891				ret = process_kf_arg_ptr_to_btf_id(env, reg, ref_t, ref_tname, ref_id, meta, i);
  9892				if (ret < 0)
  9893					return ret;
  9894				break;
  9895			case KF_ARG_PTR_TO_MEM:
  9896				resolve_ret = btf_resolve_size(btf, ref_t, &type_size);
  9897				if (IS_ERR(resolve_ret)) {
  9898					verbose(env, "arg#%d reference type('%s %s') size cannot be determined: %ld\n",
  9899						i, btf_type_str(ref_t), ref_tname, PTR_ERR(resolve_ret));
  9900					return -EINVAL;
  9901				}
  9902				ret = check_mem_reg(env, reg, regno, type_size);
  9903				if (ret < 0)
  9904					return ret;
  9905				break;
  9906			case KF_ARG_PTR_TO_MEM_SIZE:
> 9907				struct bpf_reg_state *size_reg = &regs[regno + 1];
  9908				const struct btf_param *size_arg = &args[i + 1];
  9909	
> 9910				ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1);
  9911				if (ret < 0) {
  9912					verbose(env, "arg#%d arg#%d memory, len pair leads to invalid memory access\n", i, i + 1);
  9913					return ret;
  9914				}
  9915	
  9916				if (is_kfunc_arg_const_mem_size(meta->btf, size_arg, size_reg)) {
  9917					if (meta->arg_constant.found) {
  9918						verbose(env, "verifier internal error: only one constant argument permitted\n");
  9919						return -EFAULT;
  9920					}
  9921					if (!tnum_is_const(size_reg->var_off)) {
  9922						verbose(env, "R%d must be a known constant\n", regno + 1);
  9923						return -EINVAL;
  9924					}
  9925					meta->arg_constant.found = true;
  9926					meta->arg_constant.value = size_reg->var_off.value;
  9927				}
  9928	
  9929				/* Skip next '__sz' or '__szk' argument */
  9930				i++;
  9931				break;
  9932			case KF_ARG_PTR_TO_CALLBACK:
  9933				meta->subprogno = reg->subprogno;
  9934				break;
  9935			}
  9936		}
  9937	
  9938		if (is_kfunc_release(meta) && !meta->release_regno) {
  9939			verbose(env, "release kernel function %s expects refcounted PTR_TO_BTF_ID\n",
  9940				func_name);
  9941			return -EINVAL;
  9942		}
  9943	
  9944		return 0;
  9945	}
  9946
diff mbox series

Patch

diff --git a/include/linux/filter.h b/include/linux/filter.h
index 3f6992261ec5..efa5d4a1677e 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -1548,6 +1548,9 @@  int __bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from,
 			  u32 len, u64 flags);
 int __bpf_xdp_load_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len);
 int __bpf_xdp_store_bytes(struct xdp_buff *xdp, u32 offset, void *buf, u32 len);
+void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len);
+void bpf_xdp_copy_buf(struct xdp_buff *xdp, unsigned long off,
+		      void *buf, unsigned long len, bool flush);
 #else /* CONFIG_NET */
 static inline int __bpf_skb_load_bytes(const struct sk_buff *skb, u32 offset,
 				       void *to, u32 len)
@@ -1572,6 +1575,17 @@  static inline int __bpf_xdp_store_bytes(struct xdp_buff *xdp, u32 offset,
 {
 	return -EOPNOTSUPP;
 }
+
+static inline void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len)
+{
+	return NULL;
+}
+
+static inline void *bpf_xdp_copy_buf(struct xdp_buff *xdp, unsigned long off, void *buf,
+				     unsigned long len, bool flush)
+{
+	return NULL;
+}
 #endif /* CONFIG_NET */
 
 #endif /* __LINUX_FILTER_H__ */
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index faa304c926cf..c9699304aed2 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -5329,6 +5329,11 @@  union bpf_attr {
  *		*flags* must be 0 except for skb-type dynptrs.
  *
  *		For skb-type dynptrs:
+ *		    *  All data slices of the dynptr are automatically
+ *		       invalidated after **bpf_dynptr_write**\ (). This is
+ *		       because writing may pull the skb and change the
+ *		       underlying packet buffer.
+ *
  *		    *  For *flags*, please see the flags accepted by
  *		       **bpf_skb_store_bytes**\ ().
  *	Return
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 78fad0e84ca4..33055e4c9e2e 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -2194,6 +2194,142 @@  __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid)
 	return p;
 }
 
+/**
+ * bpf_dynptr_slice - Obtain a read-only pointer to the dynptr data.
+ *
+ * For non-skb and non-xdp type dynptrs, there is no difference between
+ * bpf_dynptr_slice and bpf_dynptr_data.
+ *
+ * If the intention is to write to the data slice, please use
+ * bpf_dynptr_slice_rdwr.
+ *
+ * The user must check that the returned pointer is not null before using it.
+ *
+ * Please note that in the case of skb and xdp dynptrs, bpf_dynptr_slice
+ * does not change the underlying packet data pointers, so a call to
+ * bpf_dynptr_slice will not invalidate any ctx->data/data_end pointers in
+ * the bpf program.
+ *
+ * @ptr: The dynptr whose data slice to retrieve
+ * @offset: Offset into the dynptr
+ * @buffer: User-provided buffer to copy contents into
+ * @buffer__szk: Size (in bytes) of the buffer. This is the length of the
+ * requested slice. This must be a constant.
+ *
+ * @returns: NULL if the call failed (eg invalid dynptr), pointer to a read-only
+ * data slice (can be either direct pointer to the data or a pointer to the user
+ * provided buffer, with its contents containing the data, if unable to obtain
+ * direct pointer)
+ */
+__bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
+				   void *buffer, u32 buffer__szk)
+{
+	enum bpf_dynptr_type type;
+	u32 len = buffer__szk;
+	int err;
+
+	if (!ptr->data)
+		return 0;
+
+	err = bpf_dynptr_check_off_len(ptr, offset, len);
+	if (err)
+		return 0;
+
+	type = bpf_dynptr_get_type(ptr);
+
+	switch (type) {
+	case BPF_DYNPTR_TYPE_LOCAL:
+	case BPF_DYNPTR_TYPE_RINGBUF:
+		return ptr->data + ptr->offset + offset;
+	case BPF_DYNPTR_TYPE_SKB:
+		return skb_header_pointer(ptr->data, ptr->offset + offset, len, buffer);
+	case BPF_DYNPTR_TYPE_XDP:
+	{
+		void *xdp_ptr = bpf_xdp_pointer(ptr->data, ptr->offset + offset, len);
+		if (xdp_ptr)
+			return xdp_ptr;
+
+		bpf_xdp_copy_buf(ptr->data, ptr->offset + offset, buffer, len, false);
+		return buffer;
+	}
+	default:
+		WARN_ONCE(true, "unknown dynptr type %d\n", type);
+		return 0;
+	}
+}
+
+/**
+ * bpf_dynptr_slice_rdwr - Obtain a writable pointer to the dynptr data.
+ *
+ * For non-skb and non-xdp type dynptrs, there is no difference between
+ * bpf_dynptr_slice and bpf_dynptr_data.
+ *
+ * The returned pointer is writable and may point to either directly the dynptr
+ * data at the requested offset or to the buffer if unable to obtain a direct
+ * data pointer to (example: the requested slice is to the paged area of an skb
+ * packet). In the case where the returned pointer is to the buffer, the user
+ * is responsible for persisting writes through calling bpf_dynptr_write(). This
+ * usually looks something like this pattern:
+ *
+ * struct eth_hdr *eth = bpf_dynptr_slice_rdwr(&dynptr, 0, buffer, sizeof(buffer));
+ * if (!eth)
+ *	return TC_ACT_SHOT;
+ *
+ * // mutate eth header //
+ *
+ * if (eth == buffer)
+ *	bpf_dynptr_write(&ptr, 0, buffer, sizeof(buffer), 0);
+ *
+ * Please note that, as in the example above, the user must check that the
+ * returned pointer is not null before using it.
+ *
+ * Please also note that in the case of skb and xdp dynptrs, bpf_dynptr_slice_rdwr
+ * does not change the underlying packet data pointers, so a call to
+ * bpf_dynptr_slice_rdwr will not invalidate any ctx->data/data_end pointers in
+ * the bpf program.
+ *
+ * @ptr: The dynptr whose data slice to retrieve
+ * @offset: Offset into the dynptr
+ * @buffer: User-provided buffer to copy contents into
+ * @buffer__szk: Size (in bytes) of the buffer. This is the length of the
+ * requested slice. This must be a constant.
+ *
+ * @returns: NULL if the call failed (eg invalid dynptr), pointer to a
+ * data slice (can be either direct pointer to the data or a pointer to the user
+ * provided buffer, with its contents containing the data, if unable to obtain
+ * direct pointer)
+ */
+__bpf_kfunc void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
+					void *buffer, u32 buffer__szk)
+{
+	if (!ptr->data || bpf_dynptr_is_rdonly(ptr))
+		return 0;
+
+	/* bpf_dynptr_slice_rdwr is the same logic as bpf_dynptr_slice.
+	 *
+	 * For skb-type dynptrs, it is safe to write into the returned pointer
+	 * if the bpf program allows skb data writes. There are two possiblities
+	 * that may occur when calling bpf_dynptr_slice_rdwr:
+	 *
+	 * 1) The requested slice is in the head of the skb. In this case, the
+	 * returned pointer is directly to skb data, and if the skb is cloned, the
+	 * verifier will have uncloned it (see bpf_unclone_prologue()) already.
+	 * The pointer can be directly written into.
+	 *
+	 * 2) Some portion of the requested slice is in the paged buffer area.
+	 * In this case, the requested data will be copied out into the buffer
+	 * and the returned pointer will be a pointer to the buffer. The skb
+	 * will not be pulled. To persist the write, the user will need to call
+	 * bpf_dynptr_write(), which will pull the skb and commit the write.
+	 *
+	 * Similarly for xdp programs, if the requested slice is not across xdp
+	 * fragments, then a direct pointer will be returned, otherwise the data
+	 * will be copied out into the buffer and the user will need to call
+	 * bpf_dynptr_write() to commit changes.
+	 */
+	return bpf_dynptr_slice(ptr, offset, buffer, buffer__szk);
+}
+
 __bpf_kfunc void *bpf_cast_to_kern_ctx(void *obj)
 {
 	return obj;
@@ -2263,6 +2399,8 @@  BTF_ID_FLAGS(func, bpf_cast_to_kern_ctx)
 BTF_ID_FLAGS(func, bpf_rdonly_cast)
 BTF_ID_FLAGS(func, bpf_rcu_read_lock)
 BTF_ID_FLAGS(func, bpf_rcu_read_unlock)
+BTF_ID_FLAGS(func, bpf_dynptr_slice, KF_RET_NULL)
+BTF_ID_FLAGS(func, bpf_dynptr_slice_rdwr, KF_RET_NULL)
 BTF_SET8_END(common_btf_ids)
 
 static const struct btf_kfunc_id_set common_kfunc_set = {
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index cbb8c0178372..a9e806545c0f 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -759,6 +759,22 @@  static enum bpf_dynptr_type arg_to_dynptr_type(enum bpf_arg_type arg_type)
 	}
 }
 
+static enum bpf_type_flag get_dynptr_type_flag(enum bpf_dynptr_type type)
+{
+	switch (type) {
+	case BPF_DYNPTR_TYPE_LOCAL:
+		return DYNPTR_TYPE_LOCAL;
+	case BPF_DYNPTR_TYPE_RINGBUF:
+		return DYNPTR_TYPE_RINGBUF;
+	case BPF_DYNPTR_TYPE_SKB:
+		return DYNPTR_TYPE_SKB;
+	case BPF_DYNPTR_TYPE_XDP:
+		return DYNPTR_TYPE_XDP;
+	default:
+		return 0;
+	}
+}
+
 static bool dynptr_type_refcounted(enum bpf_dynptr_type type)
 {
 	return type == BPF_DYNPTR_TYPE_RINGBUF;
@@ -1681,6 +1697,12 @@  static bool reg_is_pkt_pointer_any(const struct bpf_reg_state *reg)
 	       reg->type == PTR_TO_PACKET_END;
 }
 
+static bool reg_is_dynptr_slice_pkt(const struct bpf_reg_state *reg)
+{
+	return base_type(reg->type) == PTR_TO_MEM &&
+		(reg->type & DYNPTR_TYPE_SKB || reg->type & DYNPTR_TYPE_XDP);
+}
+
 /* Unmodified PTR_TO_PACKET[_META,_END] register from ctx access. */
 static bool reg_is_init_pkt_pointer(const struct bpf_reg_state *reg,
 				    enum bpf_reg_type which)
@@ -7429,6 +7451,9 @@  static int check_func_proto(const struct bpf_func_proto *fn, int func_id)
 
 /* Packet data might have moved, any old PTR_TO_PACKET[_META,_END]
  * are now invalid, so turn them into unknown SCALAR_VALUE.
+ *
+ * This also applies to dynptr slices belonging to skb and xdp dynptrs,
+ * since these slices point to packet data.
  */
 static void clear_all_pkt_pointers(struct bpf_verifier_env *env)
 {
@@ -7436,7 +7461,7 @@  static void clear_all_pkt_pointers(struct bpf_verifier_env *env)
 	struct bpf_reg_state *reg;
 
 	bpf_for_each_reg_in_vstate(env->cur_state, state, reg, ({
-		if (reg_is_pkt_pointer_any(reg))
+		if (reg_is_pkt_pointer_any(reg) || reg_is_dynptr_slice_pkt(reg))
 			mark_reg_invalid(env, reg);
 	}));
 }
@@ -8688,6 +8713,11 @@  struct bpf_kfunc_call_arg_meta {
 	struct {
 		struct btf_field *field;
 	} arg_rbtree_root;
+	struct {
+		enum bpf_dynptr_type type;
+		u32 id;
+	} initialized_dynptr;
+	u64 mem_size;
 };
 
 static bool is_kfunc_acquire(struct bpf_kfunc_call_arg_meta *meta)
@@ -8761,6 +8791,19 @@  static bool is_kfunc_arg_mem_size(const struct btf *btf,
 	return __kfunc_param_match_suffix(btf, arg, "__sz");
 }
 
+static bool is_kfunc_arg_const_mem_size(const struct btf *btf,
+					const struct btf_param *arg,
+					const struct bpf_reg_state *reg)
+{
+	const struct btf_type *t;
+
+	t = btf_type_skip_modifiers(btf, arg->type, NULL);
+	if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE)
+		return false;
+
+	return __kfunc_param_match_suffix(btf, arg, "__szk");
+}
+
 static bool is_kfunc_arg_constant(const struct btf *btf, const struct btf_param *arg)
 {
 	return __kfunc_param_match_suffix(btf, arg, "__k");
@@ -8949,6 +8992,8 @@  enum special_kfunc_type {
 	KF_bpf_rbtree_first,
 	KF_bpf_dynptr_from_skb,
 	KF_bpf_dynptr_from_xdp,
+	KF_bpf_dynptr_slice,
+	KF_bpf_dynptr_slice_rdwr,
 };
 
 BTF_SET_START(special_kfunc_set)
@@ -8965,6 +9010,8 @@  BTF_ID(func, bpf_rbtree_add)
 BTF_ID(func, bpf_rbtree_first)
 BTF_ID(func, bpf_dynptr_from_skb)
 BTF_ID(func, bpf_dynptr_from_xdp)
+BTF_ID(func, bpf_dynptr_slice)
+BTF_ID(func, bpf_dynptr_slice_rdwr)
 BTF_SET_END(special_kfunc_set)
 
 BTF_ID_LIST(special_kfunc_list)
@@ -8983,6 +9030,8 @@  BTF_ID(func, bpf_rbtree_add)
 BTF_ID(func, bpf_rbtree_first)
 BTF_ID(func, bpf_dynptr_from_skb)
 BTF_ID(func, bpf_dynptr_from_xdp)
+BTF_ID(func, bpf_dynptr_slice)
+BTF_ID(func, bpf_dynptr_slice_rdwr)
 
 static bool is_kfunc_bpf_rcu_read_lock(struct bpf_kfunc_call_arg_meta *meta)
 {
@@ -9062,7 +9111,10 @@  get_kfunc_ptr_arg_type(struct bpf_verifier_env *env,
 	if (is_kfunc_arg_callback(env, meta->btf, &args[argno]))
 		return KF_ARG_PTR_TO_CALLBACK;
 
-	if (argno + 1 < nargs && is_kfunc_arg_mem_size(meta->btf, &args[argno + 1], &regs[regno + 1]))
+
+	if (argno + 1 < nargs &&
+	    (is_kfunc_arg_mem_size(meta->btf, &args[argno + 1], &regs[regno + 1]) ||
+	     is_kfunc_arg_const_mem_size(meta->btf, &args[argno + 1], &regs[regno + 1])))
 		arg_mem_size = true;
 
 	/* This is the catch all argument type of register types supported by
@@ -9745,6 +9797,18 @@  static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
 			ret = process_dynptr_func(env, regno, insn_idx, dynptr_arg_type);
 			if (ret < 0)
 				return ret;
+
+			if (!(dynptr_arg_type & MEM_UNINIT)) {
+				int id = dynptr_id(env, reg);
+
+				if (id < 0) {
+					verbose(env, "verifier internal error: failed to obtain dynptr id\n");
+					return id;
+				}
+				meta->initialized_dynptr.id = id;
+				meta->initialized_dynptr.type = dynptr_get_type(env, reg);
+			}
+
 			break;
 		}
 		case KF_ARG_PTR_TO_LIST_HEAD:
@@ -9840,12 +9904,29 @@  static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
 				return ret;
 			break;
 		case KF_ARG_PTR_TO_MEM_SIZE:
-			ret = check_kfunc_mem_size_reg(env, &regs[regno + 1], regno + 1);
+			struct bpf_reg_state *size_reg = &regs[regno + 1];
+			const struct btf_param *size_arg = &args[i + 1];
+
+			ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1);
 			if (ret < 0) {
 				verbose(env, "arg#%d arg#%d memory, len pair leads to invalid memory access\n", i, i + 1);
 				return ret;
 			}
-			/* Skip next '__sz' argument */
+
+			if (is_kfunc_arg_const_mem_size(meta->btf, size_arg, size_reg)) {
+				if (meta->arg_constant.found) {
+					verbose(env, "verifier internal error: only one constant argument permitted\n");
+					return -EFAULT;
+				}
+				if (!tnum_is_const(size_reg->var_off)) {
+					verbose(env, "R%d must be a known constant\n", regno + 1);
+					return -EINVAL;
+				}
+				meta->arg_constant.found = true;
+				meta->arg_constant.value = size_reg->var_off.value;
+			}
+
+			/* Skip next '__sz' or '__szk' argument */
 			i++;
 			break;
 		case KF_ARG_PTR_TO_CALLBACK:
@@ -10082,6 +10163,42 @@  static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 				regs[BPF_REG_0].type = PTR_TO_BTF_ID | PTR_UNTRUSTED;
 				regs[BPF_REG_0].btf = desc_btf;
 				regs[BPF_REG_0].btf_id = meta.arg_constant.value;
+			} else if (meta.func_id == special_kfunc_list[KF_bpf_dynptr_slice] ||
+				   meta.func_id == special_kfunc_list[KF_bpf_dynptr_slice_rdwr]) {
+				enum bpf_type_flag type_flag = get_dynptr_type_flag(meta.initialized_dynptr.type);
+
+				mark_reg_known_zero(env, regs, BPF_REG_0);
+
+				if (!meta.arg_constant.found) {
+					verbose(env, "verifier internal error: bpf_dynptr_slice(_rdwr) no constant size\n");
+					return -EFAULT;
+				}
+
+				regs[BPF_REG_0].mem_size = meta.arg_constant.value;
+
+				/* PTR_MAYBE_NULL will be added when is_kfunc_ret_null is checked */
+				regs[BPF_REG_0].type = PTR_TO_MEM | type_flag;
+
+				if (meta.func_id == special_kfunc_list[KF_bpf_dynptr_slice]) {
+					regs[BPF_REG_0].type |= MEM_RDONLY;
+				} else {
+					/* this will set env->seen_direct_write to true */
+					if (!may_access_direct_pkt_data(env, NULL, BPF_WRITE)) {
+						verbose(env, "the prog does not allow writes to packet data\n");
+						return -EINVAL;
+					}
+				}
+
+				if (!meta.initialized_dynptr.id) {
+					verbose(env, "verifier internal error: no dynptr id\n");
+					return -EFAULT;
+				}
+				regs[BPF_REG_0].dynptr_id = meta.initialized_dynptr.id;
+
+				/* we don't need to set BPF_REG_0's ref obj id
+				 * because packet slices are not refcounted (see
+				 * dynptr_type_refcounted)
+				 */
 			} else {
 				verbose(env, "kernel function %s unhandled dynamic return type\n",
 					meta.func_name);
diff --git a/net/core/filter.c b/net/core/filter.c
index c692046fa7f6..8f3124e06133 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -3894,8 +3894,8 @@  static const struct bpf_func_proto bpf_xdp_adjust_head_proto = {
 	.arg2_type	= ARG_ANYTHING,
 };
 
-static void bpf_xdp_copy_buf(struct xdp_buff *xdp, unsigned long off,
-			     void *buf, unsigned long len, bool flush)
+void bpf_xdp_copy_buf(struct xdp_buff *xdp, unsigned long off,
+		      void *buf, unsigned long len, bool flush)
 {
 	unsigned long ptr_len, ptr_off = 0;
 	skb_frag_t *next_frag, *end_frag;
@@ -3941,7 +3941,7 @@  static void bpf_xdp_copy_buf(struct xdp_buff *xdp, unsigned long off,
 	}
 }
 
-static void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len)
+void *bpf_xdp_pointer(struct xdp_buff *xdp, u32 offset, u32 len)
 {
 	struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
 	u32 size = xdp->data_end - xdp->data;
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index faa304c926cf..c9699304aed2 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -5329,6 +5329,11 @@  union bpf_attr {
  *		*flags* must be 0 except for skb-type dynptrs.
  *
  *		For skb-type dynptrs:
+ *		    *  All data slices of the dynptr are automatically
+ *		       invalidated after **bpf_dynptr_write**\ (). This is
+ *		       because writing may pull the skb and change the
+ *		       underlying packet buffer.
+ *
  *		    *  For *flags*, please see the flags accepted by
  *		       **bpf_skb_store_bytes**\ ().
  *	Return