From patchwork Wed Dec 7 20:55:32 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Joanne Koong X-Patchwork-Id: 13067666 X-Patchwork-Delegate: bpf@iogearbox.net 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 77004C63705 for ; Wed, 7 Dec 2022 21:19:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229668AbiLGVTg (ORCPT ); Wed, 7 Dec 2022 16:19:36 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53004 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229652AbiLGVTf (ORCPT ); Wed, 7 Dec 2022 16:19:35 -0500 Received: from 66-220-144-178.mail-mxout.facebook.com (66-220-144-178.mail-mxout.facebook.com [66.220.144.178]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9A0CC81DA6 for ; Wed, 7 Dec 2022 13:19:34 -0800 (PST) Received: by devvm15675.prn0.facebook.com (Postfix, from userid 115148) id C61A71355387; Wed, 7 Dec 2022 12:56:44 -0800 (PST) From: Joanne Koong To: bpf@vger.kernel.org Cc: andrii@kernel.org, kernel-team@meta.com, ast@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev, song@kernel.org, Joanne Koong Subject: [PATCH v2 bpf-next 1/6] bpf: Add bpf_dynptr_trim and bpf_dynptr_advance Date: Wed, 7 Dec 2022 12:55:32 -0800 Message-Id: <20221207205537.860248-2-joannelkoong@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221207205537.860248-1-joannelkoong@gmail.com> References: <20221207205537.860248-1-joannelkoong@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org X-Patchwork-Delegate: bpf@iogearbox.net Add two new helper functions: bpf_dynptr_trim and bpf_dynptr_advance. bpf_dynptr_trim decreases the size of a dynptr by the specified number of bytes (offset remains the same). bpf_dynptr_advance advances the offset of the dynptr by the specified number of bytes (size decreases correspondingly). One example where trimming / advancing the dynptr may useful is for hashing. If the dynptr points to a larger struct, it is possible to hash an individual field within the struct through dynptrs by using bpf_dynptr_advance+trim. Signed-off-by: Joanne Koong --- include/uapi/linux/bpf.h | 18 +++++++++ kernel/bpf/helpers.c | 67 ++++++++++++++++++++++++++++++++++ tools/include/uapi/linux/bpf.h | 18 +++++++++ 3 files changed, 103 insertions(+) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index a9bb98365031..c2d915601484 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -5537,6 +5537,22 @@ union bpf_attr { * *flags* is currently unused, it must be 0 for now. * Return * 0 on success, -EINVAL if flags is not 0. + * + * long bpf_dynptr_advance(struct bpf_dynptr *ptr, u32 len) + * Description + * Advance a dynptr's internal offset by *len* bytes. + * Return + * 0 on success, -EINVAL if the dynptr is invalid, -ERANGE if *len* + * exceeds the bounds of the dynptr. + * + * long bpf_dynptr_trim(struct bpf_dynptr *ptr, u32 len) + * Description + * Trim the size of memory pointed to by the dynptr by *len* bytes. + * + * The offset is unmodified. + * Return + * 0 on success, -EINVAL if the dynptr is invalid, -ERANGE if + * trying to trim more bytes than the size of the dynptr. */ #define ___BPF_FUNC_MAPPER(FN, ctx...) \ FN(unspec, 0, ##ctx) \ @@ -5753,6 +5769,8 @@ union bpf_attr { FN(cgrp_storage_delete, 211, ##ctx) \ FN(dynptr_from_skb, 212, ##ctx) \ FN(dynptr_from_xdp, 213, ##ctx) \ + FN(dynptr_advance, 214, ##ctx) \ + FN(dynptr_trim, 215, ##ctx) \ /* */ /* backwards-compatibility macros for users of __BPF_FUNC_MAPPER that don't diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 3a9c8814aaf6..fa3989047ff6 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -1429,6 +1429,13 @@ u32 bpf_dynptr_get_size(struct bpf_dynptr_kern *ptr) return ptr->size & DYNPTR_SIZE_MASK; } +static void bpf_dynptr_set_size(struct bpf_dynptr_kern *ptr, u32 new_size) +{ + u32 metadata = ptr->size & ~DYNPTR_SIZE_MASK; + + ptr->size = new_size | metadata; +} + int bpf_dynptr_check_size(u32 size) { return size > DYNPTR_MAX_SIZE ? -E2BIG : 0; @@ -1640,6 +1647,62 @@ static const struct bpf_func_proto bpf_dynptr_data_proto = { .arg3_type = ARG_CONST_ALLOC_SIZE_OR_ZERO, }; +/* For dynptrs, the offset may only be advanced and the size may only be decremented */ +static int bpf_dynptr_adjust(struct bpf_dynptr_kern *ptr, u32 off_inc, u32 sz_dec) +{ + u32 size; + + if (!ptr->data) + return -EINVAL; + + size = bpf_dynptr_get_size(ptr); + + if (sz_dec > size) + return -ERANGE; + + if (off_inc) { + u32 new_off; + + if (off_inc > size) + return -ERANGE; + + if (check_add_overflow(ptr->offset, off_inc, &new_off)) + return -ERANGE; + + ptr->offset = new_off; + } + + bpf_dynptr_set_size(ptr, size - sz_dec); + + return 0; +} + +BPF_CALL_2(bpf_dynptr_advance, struct bpf_dynptr_kern *, ptr, u32, len) +{ + return bpf_dynptr_adjust(ptr, len, len); +} + +static const struct bpf_func_proto bpf_dynptr_advance_proto = { + .func = bpf_dynptr_advance, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_DYNPTR, + .arg2_type = ARG_ANYTHING, +}; + +BPF_CALL_2(bpf_dynptr_trim, struct bpf_dynptr_kern *, ptr, u32, len) +{ + return bpf_dynptr_adjust(ptr, 0, len); +} + +static const struct bpf_func_proto bpf_dynptr_trim_proto = { + .func = bpf_dynptr_trim, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_DYNPTR, + .arg2_type = ARG_ANYTHING, +}; + const struct bpf_func_proto bpf_get_current_task_proto __weak; const struct bpf_func_proto bpf_get_current_task_btf_proto __weak; const struct bpf_func_proto bpf_probe_read_user_proto __weak; @@ -1744,6 +1807,10 @@ bpf_base_func_proto(enum bpf_func_id func_id) return &bpf_dynptr_write_proto; case BPF_FUNC_dynptr_data: return &bpf_dynptr_data_proto; + case BPF_FUNC_dynptr_advance: + return &bpf_dynptr_advance_proto; + case BPF_FUNC_dynptr_trim: + return &bpf_dynptr_trim_proto; #ifdef CONFIG_CGROUPS case BPF_FUNC_cgrp_storage_get: return &bpf_cgrp_storage_get_proto; diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index a9bb98365031..c2d915601484 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -5537,6 +5537,22 @@ union bpf_attr { * *flags* is currently unused, it must be 0 for now. * Return * 0 on success, -EINVAL if flags is not 0. + * + * long bpf_dynptr_advance(struct bpf_dynptr *ptr, u32 len) + * Description + * Advance a dynptr's internal offset by *len* bytes. + * Return + * 0 on success, -EINVAL if the dynptr is invalid, -ERANGE if *len* + * exceeds the bounds of the dynptr. + * + * long bpf_dynptr_trim(struct bpf_dynptr *ptr, u32 len) + * Description + * Trim the size of memory pointed to by the dynptr by *len* bytes. + * + * The offset is unmodified. + * Return + * 0 on success, -EINVAL if the dynptr is invalid, -ERANGE if + * trying to trim more bytes than the size of the dynptr. */ #define ___BPF_FUNC_MAPPER(FN, ctx...) \ FN(unspec, 0, ##ctx) \ @@ -5753,6 +5769,8 @@ union bpf_attr { FN(cgrp_storage_delete, 211, ##ctx) \ FN(dynptr_from_skb, 212, ##ctx) \ FN(dynptr_from_xdp, 213, ##ctx) \ + FN(dynptr_advance, 214, ##ctx) \ + FN(dynptr_trim, 215, ##ctx) \ /* */ /* backwards-compatibility macros for users of __BPF_FUNC_MAPPER that don't