size parameter, and the value of the constant matters for program safety, __k
 suffix should be used.
 
-2.2.2 __uninit Annotation
+2.2.3 __uninit Annotation
 -------------------------
 
 This annotation is used to indicate that the argument will be treated as
 annotation, the verifier will reject the program if the dynptr passed in is
 not initialized.
 
+2.2.4 __opt Annotation
+-------------------------
+
+This annotation is used to indicate that the buffer associated with an __sz or __szk
+argument may be null. If the function is passed a nullptr in place of the buffer,
+the verifier will not check that length is appropriate for the buffer. The kfunc is
+responsible for checking if this buffer is null before using it.
+
+An example is given below::
+
+        __bpf_kfunc void *bpf_dynptr_slice(..., void *buffer__opt, u32 buffer__szk)
+        {
+        ...
+        }
+
+Here, the buffer may be null. If buffer is not null, it at least of size buffer_szk.
+Either way, the returned buffer is either NULL, or of size buffer_szk. Without this
+annotation, the verifier will reject the program if a null pointer is passed in with
+a nonzero size.
+
+
 .. _BPF_kfunc_nodef:
 
 2.3 Using an existing kernel function
 
  * bpf_dynptr_slice() - Obtain a read-only pointer to the dynptr data.
  * @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.
+ * @buffer__opt: User-provided buffer to copy contents into.  May be NULL
+ * @buffer__szk: Size (in bytes) of the buffer if present. This is the
+ *               length of the requested slice. This must be a constant.
  *
  * For non-skb and non-xdp type dynptrs, there is no difference between
  * bpf_dynptr_slice and bpf_dynptr_data.
  *
+ *  If buffer__opt is NULL, the call will fail if buffer_opt was needed.
+ *
  * If the intention is to write to the data slice, please use
  * bpf_dynptr_slice_rdwr.
  *
  * direct pointer)
  */
 __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
-                                  void *buffer, u32 buffer__szk)
+                                  void *buffer__opt, u32 buffer__szk)
 {
        enum bpf_dynptr_type type;
        u32 len = buffer__szk;
        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);
+               return skb_header_pointer(ptr->data, ptr->offset + offset, len, buffer__opt);
        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;
+               if (!buffer__opt)
+                       return NULL;
+               bpf_xdp_copy_buf(ptr->data, ptr->offset + offset, buffer__opt, len, false);
+               return buffer__opt;
        }
        default:
                WARN_ONCE(true, "unknown dynptr type %d\n", type);
  * bpf_dynptr_slice_rdwr() - Obtain a writable pointer to the dynptr data.
  * @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.
+ * @buffer__opt: User-provided buffer to copy contents into. May be NULL
+ * @buffer__szk: Size (in bytes) of the buffer if present. This is the
+ *               length of the requested slice. This must be a constant.
  *
  * For non-skb and non-xdp type dynptrs, there is no difference between
  * bpf_dynptr_slice and bpf_dynptr_data.
  *
+ * If buffer__opt is NULL, the call will fail if buffer_opt was needed.
+ *
  * 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
  * direct pointer)
  */
 __bpf_kfunc void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
-                                       void *buffer, u32 buffer__szk)
+                                       void *buffer__opt, u32 buffer__szk)
 {
        if (!ptr->data || __bpf_dynptr_is_rdonly(ptr))
                return NULL;
         * 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);
+       return bpf_dynptr_slice(ptr, offset, buffer__opt, buffer__szk);
 }
 
 __bpf_kfunc int bpf_dynptr_adjust(struct bpf_dynptr_kern *ptr, u32 start, u32 end)
 
        return __kfunc_param_match_suffix(btf, arg, "__szk");
 }
 
+static bool is_kfunc_arg_optional(const struct btf *btf, const struct btf_param *arg)
+{
+       return __kfunc_param_match_suffix(btf, arg, "__opt");
+}
+
 static bool is_kfunc_arg_constant(const struct btf *btf, const struct btf_param *arg)
 {
        return __kfunc_param_match_suffix(btf, arg, "__k");
                        break;
                case KF_ARG_PTR_TO_MEM_SIZE:
                {
+                       struct bpf_reg_state *buff_reg = ®s[regno];
+                       const struct btf_param *buff_arg = &args[i];
                        struct bpf_reg_state *size_reg = ®s[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;
+                       if (!register_is_null(buff_reg) || !is_kfunc_arg_optional(meta->btf, buff_arg)) {
+                               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;
+                               }
                        }
 
                        if (is_kfunc_arg_const_mem_size(meta->btf, size_arg, size_reg)) {