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.

bpf: Replace __opt annotation with __nullable for kfuncs

The __opt annotation was originally introduced specifically for
buffer/size argument pairs in bpf_dynptr_slice() and
bpf_dynptr_slice_rdwr(), allowing the buffer pointer to be NULL while
still validating the size as a constant. The __nullable annotation
serves the same purpose but is more general and is already used
throughout the BPF subsystem for raw tracepoints, struct_ops, and other
kfuncs.

This patch unifies the two annotations by replacing __opt with
__nullable. The key change is in the verifier's
get_kfunc_ptr_arg_type() function, where mem/size pair detection is now
performed before the nullable check. This ensures that buffer/size
pairs are correctly classified as KF_ARG_PTR_TO_MEM_SIZE even when the
buffer is nullable, while adding an !arg_mem_size condition to the
nullable check prevents interference with mem/size pair handling.

When processing KF_ARG_PTR_TO_MEM_SIZE arguments, the verifier now uses
is_kfunc_arg_nullable() instead of the removed is_kfunc_arg_optional()
to determine whether to skip size validation for NULL buffers.

This is the first documentation added for the __nullable annotation,
which has been in use since it was introduced but was previously
undocumented.

No functional changes to verifier behavior - nullable buffer/size pairs
continue to work exactly as before.

Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Puranjay Mohan <puranjay@kernel.org>
Link: https://lore.kernel.org/r/20260102221513.1961781-1-puranjay@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Puranjay Mohan and committed by
Alexei Starovoitov
a069190b 7694ff8f

+44 -40
+20 -11
Documentation/bpf/kfuncs.rst
··· 178 178 annotation, the verifier will reject the program if the dynptr passed in is 179 179 not initialized. 180 180 181 - 2.3.4 __opt Annotation 182 - ------------------------- 181 + 2.3.4 __nullable Annotation 182 + --------------------------- 183 183 184 - This annotation is used to indicate that the buffer associated with an __sz or __szk 185 - argument may be null. If the function is passed a nullptr in place of the buffer, 186 - the verifier will not check that length is appropriate for the buffer. The kfunc is 187 - responsible for checking if this buffer is null before using it. 184 + This annotation is used to indicate that the pointer argument may be NULL. 185 + The verifier will allow passing NULL for such arguments. 188 186 189 187 An example is given below:: 190 188 191 - __bpf_kfunc void *bpf_dynptr_slice(..., void *buffer__opt, u32 buffer__szk) 189 + __bpf_kfunc void bpf_task_release(struct task_struct *task__nullable) 192 190 { 193 191 ... 194 192 } 195 193 196 - Here, the buffer may be null. If buffer is not null, it at least of size buffer_szk. 197 - Either way, the returned buffer is either NULL, or of size buffer_szk. Without this 198 - annotation, the verifier will reject the program if a null pointer is passed in with 199 - a nonzero size. 194 + Here, the task pointer may be NULL. The kfunc is responsible for checking if 195 + the pointer is NULL before dereferencing it. 196 + 197 + The __nullable annotation can be combined with other annotations. For example, 198 + when used with __sz or __szk annotations for memory and size pairs, the 199 + verifier will skip size validation when a NULL pointer is passed, but will 200 + still process the size argument to extract constant size information when 201 + needed:: 202 + 203 + __bpf_kfunc void *bpf_dynptr_slice(..., void *buffer__nullable, 204 + u32 buffer__szk) 205 + 206 + Here, the buffer may be NULL. If the buffer is not NULL, it must be at least 207 + buffer__szk bytes in size. The kfunc is responsible for checking if the buffer 208 + is NULL before using it. 200 209 201 210 2.3.5 __str Annotation 202 211 ----------------------------
+1 -1
include/linux/bpf.h
··· 1434 1434 int __bpf_dynptr_write(const struct bpf_dynptr_kern *dst, u64 offset, 1435 1435 void *src, u64 len, u64 flags); 1436 1436 void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr *p, u64 offset, 1437 - void *buffer__opt, u64 buffer__szk); 1437 + void *buffer__nullable, u64 buffer__szk); 1438 1438 1439 1439 static inline int bpf_dynptr_check_off_len(const struct bpf_dynptr_kern *ptr, u64 offset, u64 len) 1440 1440 {
+14 -14
kernel/bpf/helpers.c
··· 2709 2709 * bpf_dynptr_slice() - Obtain a read-only pointer to the dynptr data. 2710 2710 * @p: The dynptr whose data slice to retrieve 2711 2711 * @offset: Offset into the dynptr 2712 - * @buffer__opt: User-provided buffer to copy contents into. May be NULL 2712 + * @buffer__nullable: User-provided buffer to copy contents into. May be NULL 2713 2713 * @buffer__szk: Size (in bytes) of the buffer if present. This is the 2714 2714 * length of the requested slice. This must be a constant. 2715 2715 * 2716 2716 * For non-skb and non-xdp type dynptrs, there is no difference between 2717 2717 * bpf_dynptr_slice and bpf_dynptr_data. 2718 2718 * 2719 - * If buffer__opt is NULL, the call will fail if buffer_opt was needed. 2719 + * If buffer__nullable is NULL, the call will fail if buffer_opt was needed. 2720 2720 * 2721 2721 * If the intention is to write to the data slice, please use 2722 2722 * bpf_dynptr_slice_rdwr. ··· 2734 2734 * direct pointer) 2735 2735 */ 2736 2736 __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr *p, u64 offset, 2737 - void *buffer__opt, u64 buffer__szk) 2737 + void *buffer__nullable, u64 buffer__szk) 2738 2738 { 2739 2739 const struct bpf_dynptr_kern *ptr = (struct bpf_dynptr_kern *)p; 2740 2740 enum bpf_dynptr_type type; ··· 2755 2755 case BPF_DYNPTR_TYPE_RINGBUF: 2756 2756 return ptr->data + ptr->offset + offset; 2757 2757 case BPF_DYNPTR_TYPE_SKB: 2758 - if (buffer__opt) 2759 - return skb_header_pointer(ptr->data, ptr->offset + offset, len, buffer__opt); 2758 + if (buffer__nullable) 2759 + return skb_header_pointer(ptr->data, ptr->offset + offset, len, buffer__nullable); 2760 2760 else 2761 2761 return skb_pointer_if_linear(ptr->data, ptr->offset + offset, len); 2762 2762 case BPF_DYNPTR_TYPE_XDP: ··· 2765 2765 if (!IS_ERR_OR_NULL(xdp_ptr)) 2766 2766 return xdp_ptr; 2767 2767 2768 - if (!buffer__opt) 2768 + if (!buffer__nullable) 2769 2769 return NULL; 2770 - bpf_xdp_copy_buf(ptr->data, ptr->offset + offset, buffer__opt, len, false); 2771 - return buffer__opt; 2770 + bpf_xdp_copy_buf(ptr->data, ptr->offset + offset, buffer__nullable, len, false); 2771 + return buffer__nullable; 2772 2772 } 2773 2773 case BPF_DYNPTR_TYPE_SKB_META: 2774 2774 return bpf_skb_meta_pointer(ptr->data, ptr->offset + offset); 2775 2775 case BPF_DYNPTR_TYPE_FILE: 2776 - err = bpf_file_fetch_bytes(ptr->data, offset, buffer__opt, buffer__szk); 2777 - return err ? NULL : buffer__opt; 2776 + err = bpf_file_fetch_bytes(ptr->data, offset, buffer__nullable, buffer__szk); 2777 + return err ? NULL : buffer__nullable; 2778 2778 default: 2779 2779 WARN_ONCE(true, "unknown dynptr type %d\n", type); 2780 2780 return NULL; ··· 2785 2785 * bpf_dynptr_slice_rdwr() - Obtain a writable pointer to the dynptr data. 2786 2786 * @p: The dynptr whose data slice to retrieve 2787 2787 * @offset: Offset into the dynptr 2788 - * @buffer__opt: User-provided buffer to copy contents into. May be NULL 2788 + * @buffer__nullable: User-provided buffer to copy contents into. May be NULL 2789 2789 * @buffer__szk: Size (in bytes) of the buffer if present. This is the 2790 2790 * length of the requested slice. This must be a constant. 2791 2791 * 2792 2792 * For non-skb and non-xdp type dynptrs, there is no difference between 2793 2793 * bpf_dynptr_slice and bpf_dynptr_data. 2794 2794 * 2795 - * If buffer__opt is NULL, the call will fail if buffer_opt was needed. 2795 + * If buffer__nullable is NULL, the call will fail if buffer_opt was needed. 2796 2796 * 2797 2797 * The returned pointer is writable and may point to either directly the dynptr 2798 2798 * data at the requested offset or to the buffer if unable to obtain a direct ··· 2824 2824 * direct pointer) 2825 2825 */ 2826 2826 __bpf_kfunc void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr *p, u64 offset, 2827 - void *buffer__opt, u64 buffer__szk) 2827 + void *buffer__nullable, u64 buffer__szk) 2828 2828 { 2829 2829 const struct bpf_dynptr_kern *ptr = (struct bpf_dynptr_kern *)p; 2830 2830 ··· 2853 2853 * will be copied out into the buffer and the user will need to call 2854 2854 * bpf_dynptr_write() to commit changes. 2855 2855 */ 2856 - return bpf_dynptr_slice(p, offset, buffer__opt, buffer__szk); 2856 + return bpf_dynptr_slice(p, offset, buffer__nullable, buffer__szk); 2857 2857 } 2858 2858 2859 2859 __bpf_kfunc int bpf_dynptr_adjust(const struct bpf_dynptr *p, u64 start, u64 end)
+9 -14
kernel/bpf/verifier.c
··· 12086 12086 return btf_param_match_suffix(btf, arg, "__szk"); 12087 12087 } 12088 12088 12089 - static bool is_kfunc_arg_optional(const struct btf *btf, const struct btf_param *arg) 12090 - { 12091 - return btf_param_match_suffix(btf, arg, "__opt"); 12092 - } 12093 - 12094 12089 static bool is_kfunc_arg_constant(const struct btf *btf, const struct btf_param *arg) 12095 12090 { 12096 12091 return btf_param_match_suffix(btf, arg, "__k"); ··· 12505 12510 if (meta->func_id == special_kfunc_list[KF_bpf_cast_to_kern_ctx]) 12506 12511 return KF_ARG_PTR_TO_CTX; 12507 12512 12513 + if (argno + 1 < nargs && 12514 + (is_kfunc_arg_mem_size(meta->btf, &args[argno + 1], &regs[regno + 1]) || 12515 + is_kfunc_arg_const_mem_size(meta->btf, &args[argno + 1], &regs[regno + 1]))) 12516 + arg_mem_size = true; 12517 + 12508 12518 /* In this function, we verify the kfunc's BTF as per the argument type, 12509 12519 * leaving the rest of the verification with respect to the register 12510 12520 * type to our caller. When a set of conditions hold in the BTF type of ··· 12518 12518 if (btf_is_prog_ctx_type(&env->log, meta->btf, t, resolve_prog_type(env->prog), argno)) 12519 12519 return KF_ARG_PTR_TO_CTX; 12520 12520 12521 - if (is_kfunc_arg_nullable(meta->btf, &args[argno]) && register_is_null(reg)) 12521 + if (is_kfunc_arg_nullable(meta->btf, &args[argno]) && register_is_null(reg) && 12522 + !arg_mem_size) 12522 12523 return KF_ARG_PTR_TO_NULL; 12523 12524 12524 12525 if (is_kfunc_arg_alloc_obj(meta->btf, &args[argno])) ··· 12575 12574 12576 12575 if (is_kfunc_arg_callback(env, meta->btf, &args[argno])) 12577 12576 return KF_ARG_PTR_TO_CALLBACK; 12578 - 12579 - if (argno + 1 < nargs && 12580 - (is_kfunc_arg_mem_size(meta->btf, &args[argno + 1], &regs[regno + 1]) || 12581 - is_kfunc_arg_const_mem_size(meta->btf, &args[argno + 1], &regs[regno + 1]))) 12582 - arg_mem_size = true; 12583 12577 12584 12578 /* This is the catch all argument type of register types supported by 12585 12579 * check_helper_mem_access. However, we only allow when argument type is ··· 13245 13249 } 13246 13250 13247 13251 if ((register_is_null(reg) || type_may_be_null(reg->type)) && 13248 - !is_kfunc_arg_nullable(meta->btf, &args[i]) && 13249 - !is_kfunc_arg_optional(meta->btf, &args[i])) { 13252 + !is_kfunc_arg_nullable(meta->btf, &args[i])) { 13250 13253 verbose(env, "Possibly NULL pointer passed to trusted arg%d\n", i); 13251 13254 return -EACCES; 13252 13255 } ··· 13561 13566 struct bpf_reg_state *size_reg = &regs[regno + 1]; 13562 13567 const struct btf_param *size_arg = &args[i + 1]; 13563 13568 13564 - if (!register_is_null(buff_reg) || !is_kfunc_arg_optional(meta->btf, buff_arg)) { 13569 + if (!register_is_null(buff_reg) || !is_kfunc_arg_nullable(meta->btf, buff_arg)) { 13565 13570 ret = check_kfunc_mem_size_reg(env, size_reg, regno + 1); 13566 13571 if (ret < 0) { 13567 13572 verbose(env, "arg#%d arg#%d memory, len pair leads to invalid memory access\n", i, i + 1);