bpf: Add bpf_dynptr_from_mem for local dynptrs
authorJoanne Koong <joannelkoong@gmail.com>
Mon, 23 May 2022 21:07:08 +0000 (14:07 -0700)
committerAndrii Nakryiko <andrii@kernel.org>
Mon, 23 May 2022 21:31:24 +0000 (14:31 -0700)
This patch adds a new api bpf_dynptr_from_mem:

long bpf_dynptr_from_mem(void *data, u32 size, u64 flags, struct bpf_dynptr *ptr);

which initializes a dynptr to point to a bpf program's local memory. For now
only local memory that is of reg type PTR_TO_MAP_VALUE is supported.

Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20220523210712.3641569-3-joannelkoong@gmail.com
include/uapi/linux/bpf.h
kernel/bpf/helpers.c
kernel/bpf/verifier.c
tools/include/uapi/linux/bpf.h

index 610944c..9be3644 100644 (file)
@@ -5178,6 +5178,17 @@ union bpf_attr {
  *             Dynamically cast a *sk* pointer to a *mptcp_sock* pointer.
  *     Return
  *             *sk* if casting is valid, or **NULL** otherwise.
+ *
+ * long bpf_dynptr_from_mem(void *data, u32 size, u64 flags, struct bpf_dynptr *ptr)
+ *     Description
+ *             Get a dynptr to local memory *data*.
+ *
+ *             *data* must be a ptr to a map value.
+ *             The maximum *size* supported is DYNPTR_MAX_SIZE.
+ *             *flags* is currently unused.
+ *     Return
+ *             0 on success, -E2BIG if the size exceeds DYNPTR_MAX_SIZE,
+ *             -EINVAL if flags is not 0.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -5377,6 +5388,7 @@ union bpf_attr {
        FN(kptr_xchg),                  \
        FN(map_lookup_percpu_elem),     \
        FN(skc_to_mptcp_sock),          \
+       FN(dynptr_from_mem),            \
        /* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
index bad9613..d3e935c 100644 (file)
@@ -1412,6 +1412,69 @@ const struct bpf_func_proto bpf_kptr_xchg_proto = {
        .arg2_btf_id  = BPF_PTR_POISON,
 };
 
+/* Since the upper 8 bits of dynptr->size is reserved, the
+ * maximum supported size is 2^24 - 1.
+ */
+#define DYNPTR_MAX_SIZE        ((1UL << 24) - 1)
+#define DYNPTR_TYPE_SHIFT      28
+
+static void bpf_dynptr_set_type(struct bpf_dynptr_kern *ptr, enum bpf_dynptr_type type)
+{
+       ptr->size |= type << DYNPTR_TYPE_SHIFT;
+}
+
+static int bpf_dynptr_check_size(u32 size)
+{
+       return size > DYNPTR_MAX_SIZE ? -E2BIG : 0;
+}
+
+static void bpf_dynptr_init(struct bpf_dynptr_kern *ptr, void *data,
+                           enum bpf_dynptr_type type, u32 offset, u32 size)
+{
+       ptr->data = data;
+       ptr->offset = offset;
+       ptr->size = size;
+       bpf_dynptr_set_type(ptr, type);
+}
+
+static void bpf_dynptr_set_null(struct bpf_dynptr_kern *ptr)
+{
+       memset(ptr, 0, sizeof(*ptr));
+}
+
+BPF_CALL_4(bpf_dynptr_from_mem, void *, data, u32, size, u64, flags, struct bpf_dynptr_kern *, ptr)
+{
+       int err;
+
+       err = bpf_dynptr_check_size(size);
+       if (err)
+               goto error;
+
+       /* flags is currently unsupported */
+       if (flags) {
+               err = -EINVAL;
+               goto error;
+       }
+
+       bpf_dynptr_init(ptr, data, BPF_DYNPTR_TYPE_LOCAL, 0, size);
+
+       return 0;
+
+error:
+       bpf_dynptr_set_null(ptr);
+       return err;
+}
+
+const struct bpf_func_proto bpf_dynptr_from_mem_proto = {
+       .func           = bpf_dynptr_from_mem,
+       .gpl_only       = false,
+       .ret_type       = RET_INTEGER,
+       .arg1_type      = ARG_PTR_TO_UNINIT_MEM,
+       .arg2_type      = ARG_CONST_SIZE_OR_ZERO,
+       .arg3_type      = ARG_ANYTHING,
+       .arg4_type      = ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL | MEM_UNINIT,
+};
+
 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;
@@ -1466,6 +1529,8 @@ bpf_base_func_proto(enum bpf_func_id func_id)
                return &bpf_loop_proto;
        case BPF_FUNC_strncmp:
                return &bpf_strncmp_proto;
+       case BPF_FUNC_dynptr_from_mem:
+               return &bpf_dynptr_from_mem_proto;
        default:
                break;
        }
index 1fd0b81..b657d46 100644 (file)
@@ -7204,6 +7204,12 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
                err = __check_func_call(env, insn, insn_idx_p, meta.subprogno,
                                        set_loop_callback_state);
                break;
+       case BPF_FUNC_dynptr_from_mem:
+               if (regs[BPF_REG_1].type != PTR_TO_MAP_VALUE) {
+                       verbose(env, "Unsupported reg type %s for bpf_dynptr_from_mem data\n",
+                               reg_type_str(env, regs[BPF_REG_1].type));
+                       return -EACCES;
+               }
        }
 
        if (err)
index 610944c..9be3644 100644 (file)
@@ -5178,6 +5178,17 @@ union bpf_attr {
  *             Dynamically cast a *sk* pointer to a *mptcp_sock* pointer.
  *     Return
  *             *sk* if casting is valid, or **NULL** otherwise.
+ *
+ * long bpf_dynptr_from_mem(void *data, u32 size, u64 flags, struct bpf_dynptr *ptr)
+ *     Description
+ *             Get a dynptr to local memory *data*.
+ *
+ *             *data* must be a ptr to a map value.
+ *             The maximum *size* supported is DYNPTR_MAX_SIZE.
+ *             *flags* is currently unused.
+ *     Return
+ *             0 on success, -E2BIG if the size exceeds DYNPTR_MAX_SIZE,
+ *             -EINVAL if flags is not 0.
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -5377,6 +5388,7 @@ union bpf_attr {
        FN(kptr_xchg),                  \
        FN(map_lookup_percpu_elem),     \
        FN(skc_to_mptcp_sock),          \
+       FN(dynptr_from_mem),            \
        /* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper