From nobody Wed Dec 17 21:14:52 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 48EEDC77B75 for ; Tue, 18 Apr 2023 01:41:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230070AbjDRBlG (ORCPT ); Mon, 17 Apr 2023 21:41:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53074 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229838AbjDRBk6 (ORCPT ); Mon, 17 Apr 2023 21:40:58 -0400 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DD49A5279 for ; Mon, 17 Apr 2023 18:40:56 -0700 (PDT) Received: by mail-yb1-xb4a.google.com with SMTP id 84-20020a251457000000b00b8f59a09e1fso11379682ybu.5 for ; Mon, 17 Apr 2023 18:40:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20221208; t=1681782055; x=1684374055; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=WjDDJW+GHYKcFKqxIZ+BL5gG6uAgwAshe83Msya4lZk=; b=NlOg4YH4aS6Jqn57OyoDroboiAPLs6DTDbrvGBZbNhHR10+m/WdBxEvhRm0xAGWYMp f9bQ4TPfKNqD7xy2km5YYg0yfmm/J4l/kQ9agtg79DmAEtF6sxG6YZL0P09rDUitmuJT kXa0oepXRXmncPOGL54W0tDR+viFUV7oZolHNgNGYjHS3czEp7wnPo/SRvxpM/LZwElo qtthAQBa5tFhv3tSpPuuHV9MiyJGbjl+TBimhWKh7FOD20dvSUG3x1gbWqH++RfHPFb1 qQwmzd7SdI+qv/79iFoti9uokwoWrsiLI5aQMJ9aVjw//OVLtTGARlAgnsUtTxomxwzx BL5A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1681782055; x=1684374055; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=WjDDJW+GHYKcFKqxIZ+BL5gG6uAgwAshe83Msya4lZk=; b=R0blVFL9eTfHO/Qh4xsm+4rggNzi0UKNd4hCUKBYrf+krR8Pib7cQOKgF+jtF5i3lB 1xsBJxm7qMKltReMExYxNLYUeRxDW+dkP5yiecwN2gCHvfKJaaqPsBWmrQa/Mg83a2dh W8Tsyc7VfDMo8oWkgTQlYqtgEsGrhzqJ01/OKi27bMX94B7d8yDcOLMEhZhnLFK6JV75 BwvEiG6/7k0A3HJeRvSB9hN0wZt94rq2Uu3KkEj1AgrqEt7y6gxKCcnaEI0h+b+HVP0R BtFZrzAbeijvOBECmmRPZi6TTBPSqSQ5TEJ7BLlioY4lIOX3qWSQFl5pZy0OrGUQmk7d skEw== X-Gm-Message-State: AAQBX9cGW7FhnF13PLF1qVMwMGRNjNCj4ccc1vwv+BY2DJ16uG9SbYyq sBh68cMUtaCuFLPRlmTV0BymZJw/kNI= X-Google-Smtp-Source: AKy350b6WbLfFOIpMTkeOSl2mlRWjzNHXOblepg4SNDqaWtzBw/uJNst+ZDP03Eg7cvOh/SLsMoc8N9aowY= X-Received: from drosen.mtv.corp.google.com ([2620:15c:211:201:e67a:98b0:942d:86aa]) (user=drosen job=sendgmr) by 2002:a05:690c:706:b0:545:5f92:f7ee with SMTP id bs6-20020a05690c070600b005455f92f7eemr11333022ywb.2.1681782055788; Mon, 17 Apr 2023 18:40:55 -0700 (PDT) Date: Mon, 17 Apr 2023 18:40:02 -0700 In-Reply-To: <20230418014037.2412394-1-drosen@google.com> Mime-Version: 1.0 References: <20230418014037.2412394-1-drosen@google.com> X-Mailer: git-send-email 2.40.0.634.g4ca3ef3211-goog Message-ID: <20230418014037.2412394-3-drosen@google.com> Subject: [RFC PATCH v3 02/37] bpf: Allow NULL buffers in bpf_dynptr_slice(_rw) From: Daniel Rosenberg To: Miklos Szeredi , bpf@vger.kernel.org, Alexei Starovoitov Cc: Amir Goldstein , linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, Daniel Borkmann , John Fastabend , Andrii Nakryiko , Martin KaFai Lau , Song Liu , Yonghong Song , KP Singh , Stanislav Fomichev , Hao Luo , Jiri Olsa , Shuah Khan , Jonathan Corbet , Joanne Koong , Mykola Lysenko , kernel-team@android.com, Daniel Rosenberg Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" bpf_dynptr_slice(_rw) uses a user provided buffer if it can not provide a pointer to a block of contiguous memory. This buffer is unused in the case of local dynptrs, and may be unused in other cases as well. There is no need to require the buffer, as the kfunc can just return NULL if it was needed and not provided. This adds another kfunc annotation, __opt, which combines with __sz and __szk to allow the buffer associated with the size to be NULL. If the buffer is NULL, the verifier does not check that the buffer is of sufficient size. Signed-off-by: Daniel Rosenberg --- Documentation/bpf/kfuncs.rst | 23 ++++++++++++++++++++++- kernel/bpf/helpers.c | 32 ++++++++++++++++++++------------ kernel/bpf/verifier.c | 19 +++++++++++++++++++ 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst index ea2516374d92..7a3d9de5f315 100644 --- a/Documentation/bpf/kfuncs.rst +++ b/Documentation/bpf/kfuncs.rst @@ -100,7 +100,7 @@ Hence, whenever a constant scalar argument is accepted = by a kfunc which is not a size parameter, and the value of the constant matters for program safety, = __k suffix should be used. =20 -2.2.2 __uninit Annotation +2.2.3 __uninit Annotation ------------------------- =20 This annotation is used to indicate that the argument will be treated as @@ -117,6 +117,27 @@ Here, the dynptr will be treated as an uninitialized d= ynptr. Without this annotation, the verifier will reject the program if the dynptr passed in is not initialized. =20 +2.2.4 __opt Annotation +------------------------- + +This annotation is used to indicate that the buffer associated with an __s= z 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 buf= fer__szk) + { + ... + } + +Here, the buffer may be null. If buffer is not null, it at least of size b= uffer_szk. +Either way, the returned buffer is either NULL, or of size buffer_szk. Wit= hout this +annotation, the verifier will reject the program if a null pointer is pass= ed in with +a nonzero size. + + .. _BPF_kfunc_nodef: =20 2.3 Using an existing kernel function diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 00e5fb0682ac..bfb75ecacb76 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -2167,13 +2167,15 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s= 32 pid) * 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. * @@ -2190,7 +2192,7 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s32= pid) * 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 =3D buffer__szk; @@ -2210,15 +2212,19 @@ __bpf_kfunc void *bpf_dynptr_slice(const struct bpf= _dynptr_kern *ptr, u32 offset 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); + if (!buffer__opt) + return NULL; + return skb_header_pointer(ptr->data, ptr->offset + offset, len, buffer__= opt); case BPF_DYNPTR_TYPE_XDP: { void *xdp_ptr =3D bpf_xdp_pointer(ptr->data, ptr->offset + offset, len); if (xdp_ptr) return xdp_ptr; =20 - 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, fals= e); + return buffer__opt; } default: WARN_ONCE(true, "unknown dynptr type %d\n", type); @@ -2230,13 +2236,15 @@ __bpf_kfunc void *bpf_dynptr_slice(const struct bpf= _dynptr_kern *ptr, u32 offset * 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 d= ynptr * data at the requested offset or to the buffer if unable to obtain a dir= ect * data pointer to (example: the requested slice is to the paged area of a= n skb @@ -2267,7 +2275,7 @@ __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_d= ynptr_kern *ptr, u32 offset * 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; @@ -2294,7 +2302,7 @@ __bpf_kfunc void *bpf_dynptr_slice_rdwr(const struct = bpf_dynptr_kern *ptr, u32 o * 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); } =20 __bpf_kfunc void *bpf_cast_to_kern_ctx(void *obj) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index ebc638bfed87..fd959824469d 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -9387,6 +9387,19 @@ static bool is_kfunc_arg_const_mem_size(const struct= btf *btf, return __kfunc_param_match_suffix(btf, arg, "__szk"); } =20 +static bool is_kfunc_arg_optional(const struct btf *btf, + const struct btf_param *arg, + const struct bpf_reg_state *reg) +{ + const struct btf_type *t; + + t =3D btf_type_skip_modifiers(btf, arg->type, NULL); + if (!btf_type_is_ptr(t) || reg->type !=3D SCALAR_VALUE || reg->umax_value= > 0) + return false; + + 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"); @@ -10453,10 +10466,16 @@ static int check_kfunc_args(struct bpf_verifier_e= nv *env, struct bpf_kfunc_call_ break; case KF_ARG_PTR_TO_MEM_SIZE: { + struct bpf_reg_state *buff_reg =3D ®s[regno]; + const struct btf_param *buff_arg =3D &args[i]; struct bpf_reg_state *size_reg =3D ®s[regno + 1]; const struct btf_param *size_arg =3D &args[i + 1]; =20 ret =3D check_kfunc_mem_size_reg(env, size_reg, regno + 1); + if (ret < 0 && is_kfunc_arg_optional(meta->btf, buff_arg, buff_reg)) { + verbose(env, "error was %d", ret); + ret =3D 0; + } if (ret < 0) { verbose(env, "arg#%d arg#%d memory, len pair leads to invalid memory a= ccess\n", i, i + 1); return ret; --=20 2.40.0.634.g4ca3ef3211-goog