Thread (44 messages) 44 messages, 5 authors, 2022-04-01

Re: [PATCH bpf-next v3 02/17] bpf: introduce hid program type

From: Song Liu <song@kernel.org>
Date: 2022-03-18 20:48:41
Also in: bpf, linux-doc, linux-kselftest, lkml, netdev

On Fri, Mar 18, 2022 at 9:16 AM Benjamin Tissoires
[off-list ref] wrote:
[...]
quoted hunk ↗ jump to hunk
diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h
new file mode 100644
index 000000000000..9c8dbd389995
--- /dev/null
+++ b/include/linux/bpf-hid.h
[...]
+
+struct hid_bpf_ctx_kern {
+       enum hid_bpf_event type;        /* read-only */
+       struct hid_device *hdev;        /* read-only */
+
+       u16 size;                       /* used size in data (RW) */
+       u8 *data;                       /* data buffer (RW) */
+       u32 allocated_size;             /* allocated size of data (RO) */
Why u16 size vs. u32 allocated_size? Also, maybe shuffle the members
to remove some holes?
+
+       s32 retval;                     /* in use when BPF_HID_ATTACH_USER_EVENT (RW) */
+};
+
[...]
quoted hunk ↗ jump to hunk
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
We need to mirror these changes to tools/include/uapi/linux/bpf.h.
quoted hunk ↗ jump to hunk
index 99fab54ae9c0..0e8438e93768 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -952,6 +952,7 @@ enum bpf_prog_type {
        BPF_PROG_TYPE_LSM,
        BPF_PROG_TYPE_SK_LOOKUP,
        BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
+       BPF_PROG_TYPE_HID,
 };
[...]
quoted hunk ↗ jump to hunk
+
 /* When BPF ldimm64's insn[0].src_reg != 0 then this can have
  * the following extensions:
  *
@@ -5129,6 +5145,16 @@ union bpf_attr {
  *             The **hash_algo** is returned on success,
  *             **-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if
  *             invalid arguments are passed.
+ *
+ * void *bpf_hid_get_data(void *ctx, u64 offset, u64 size)
+ *     Description
+ *             Returns a pointer to the data associated with context at the given
+ *             offset and size (in bytes).
+ *
+ *             Note: the returned pointer is refcounted and must be dereferenced
+ *             by a call to bpf_hid_discard;
+ *     Return
+ *             The pointer to the data. On error, a null value is returned.
Please use annotations like *size*, **NULL**.
quoted hunk ↗ jump to hunk
  */
 #define __BPF_FUNC_MAPPER(FN)          \
        FN(unspec),                     \
@@ -5325,6 +5351,7 @@ union bpf_attr {
        FN(copy_from_user_task),        \
        FN(skb_set_tstamp),             \
        FN(ima_file_hash),              \
+       FN(hid_get_data),               \
        /* */

 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
@@ -5925,6 +5952,10 @@ struct bpf_link_info {
                struct {
                        __u32 ifindex;
                } xdp;
+               struct  {
+                       __s32 hidraw_number;
+                       __u32 attach_type;
+               } hid;
        };
 } __attribute__((aligned(8)));
diff --git a/include/uapi/linux/bpf_hid.h b/include/uapi/linux/bpf_hid.h
new file mode 100644
index 000000000000..64a8b9dd8809
--- /dev/null
+++ b/include/uapi/linux/bpf_hid.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */
+
+/*
+ *  HID BPF public headers
+ *
+ *  Copyright (c) 2022 Benjamin Tissoires
+ */
+
+#ifndef _UAPI__LINUX_BPF_HID_H__
+#define _UAPI__LINUX_BPF_HID_H__
+
+#include <linux/types.h>
+
+enum hid_bpf_event {
+       HID_BPF_UNDEF = 0,
+       HID_BPF_DEVICE_EVENT,           /* when attach type is BPF_HID_DEVICE_EVENT */
+       HID_BPF_RDESC_FIXUP,            /* ................... BPF_HID_RDESC_FIXUP */
+       HID_BPF_USER_EVENT,             /* ................... BPF_HID_USER_EVENT */
Why don't we have a DRIVER_EVENT type here?
[...]
+
+BPF_CALL_3(bpf_hid_get_data, struct hid_bpf_ctx_kern*, ctx, u64, offset, u64, size)
+{
+       if (!size)
+               return 0UL;
+
+       if (offset + size > ctx->allocated_size)
+               return 0UL;
+
+       return (unsigned long)(ctx->data + offset);
+}
+
+static const struct bpf_func_proto bpf_hid_get_data_proto = {
+       .func      = bpf_hid_get_data,
+       .gpl_only  = true,
+       .ret_type  = RET_PTR_TO_ALLOC_MEM_OR_NULL,
+       .arg1_type = ARG_PTR_TO_CTX,
+       .arg2_type = ARG_ANYTHING,
+       .arg3_type = ARG_CONST_ALLOC_SIZE_OR_ZERO,
I think we should use ARG_CONST_SIZE or ARG_CONST_SIZE_OR_ZERO?
+};
+
+static const struct bpf_func_proto *
+hid_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
+{
+       switch (func_id) {
+       case BPF_FUNC_hid_get_data:
+               return &bpf_hid_get_data_proto;
+       default:
+               return bpf_base_func_proto(func_id);
+       }
+}
[...]
+
+static int hid_bpf_prog_test_run(struct bpf_prog *prog,
+                                const union bpf_attr *attr,
+                                union bpf_attr __user *uattr)
+{
+       struct hid_device *hdev = NULL;
+       struct bpf_prog_array *progs;
+       bool valid_prog = false;
+       int i;
+       int target_fd, ret;
+       void __user *data_out = u64_to_user_ptr(attr->test.data_out);
+       void __user *data_in = u64_to_user_ptr(attr->test.data_in);
+       u32 user_size_in = attr->test.data_size_in;
+       u32 user_size_out = attr->test.data_size_out;
+       u32 allocated_size = max(user_size_in, user_size_out);
+       struct hid_bpf_ctx_kern ctx = {
+               .type = HID_BPF_USER_EVENT,
+               .allocated_size = allocated_size,
+       };
+
+       if (!hid_hooks.hdev_from_fd)
+               return -EOPNOTSUPP;
+
+       if (attr->test.ctx_size_in != sizeof(int))
+               return -EINVAL;
ctx_size_in is always 4 bytes?
quoted hunk ↗ jump to hunk
+
+       if (allocated_size > HID_MAX_BUFFER_SIZE)
+               return -E2BIG;
+
+       if (copy_from_user(&target_fd, (void *)attr->test.ctx_in, attr->test.ctx_size_in))
+               return -EFAULT;
+
+       hdev = hid_hooks.hdev_from_fd(target_fd);
+       if (IS_ERR(hdev))
+               return PTR_ERR(hdev);
+
+       if (allocated_size) {
+               ctx.data = kzalloc(allocated_size, GFP_KERNEL);
+               if (!ctx.data)
+                       return -ENOMEM;
+
+               ctx.allocated_size = allocated_size;
+       }
+       ctx.hdev = hdev;
+
+       ret = mutex_lock_interruptible(&bpf_hid_mutex);
+       if (ret)
+               return ret;
+
+       /* check if the given program is of correct type and registered */
+       progs = rcu_dereference_protected(hdev->bpf.run_array[BPF_HID_ATTACH_USER_EVENT],
+                                         lockdep_is_held(&bpf_hid_mutex));
+       if (!progs) {
+               ret = -EFAULT;
+               goto unlock;
+       }
+
+       for (i = 0; i < bpf_prog_array_length(progs); i++) {
+               if (progs->items[i].prog == prog) {
+                       valid_prog = true;
+                       break;
+               }
+       }
+
+       if (!valid_prog) {
+               ret = -EINVAL;
+               goto unlock;
+       }
+
+       /* copy data_in from userspace */
+       if (user_size_in) {
+               if (copy_from_user(ctx.data, data_in, user_size_in)) {
+                       ret = -EFAULT;
+                       goto unlock;
+               }
+
+               ctx.size = user_size_in;
+       }
+
+       migrate_disable();
+
+       ret = bpf_prog_run(prog, &ctx);
+
+       migrate_enable();
+
+       if (user_size_out && data_out) {
+               user_size_out = min3(user_size_out, (u32)ctx.size, allocated_size);
+
+               if (copy_to_user(data_out, ctx.data, user_size_out)) {
+                       ret = -EFAULT;
+                       goto unlock;
+               }
+
+               if (copy_to_user(&uattr->test.data_size_out,
+                                &user_size_out,
+                                sizeof(user_size_out))) {
+                       ret = -EFAULT;
+                       goto unlock;
+               }
+       }
+
+       if (copy_to_user(&uattr->test.retval, &ctx.retval, sizeof(ctx.retval)))
+               ret = -EFAULT;
+
+unlock:
+       kfree(ctx.data);
+
+       mutex_unlock(&bpf_hid_mutex);
+       return ret;
+}
+
+const struct bpf_prog_ops hid_prog_ops = {
+       .test_run = hid_bpf_prog_test_run,
+};
+
+int bpf_hid_init(struct hid_device *hdev)
+{
+       int type;
+
+       for (type = 0; type < MAX_BPF_HID_ATTACH_TYPE; type++)
+               INIT_LIST_HEAD(&hdev->bpf.links[type]);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(bpf_hid_init);
+
+void bpf_hid_exit(struct hid_device *hdev)
+{
+       enum bpf_hid_attach_type type;
+       struct bpf_hid_link *hid_link;
+
+       mutex_lock(&bpf_hid_mutex);
+       for (type = 0; type < MAX_BPF_HID_ATTACH_TYPE; type++) {
+               bpf_hid_run_array_detach(hdev, type);
+               list_for_each_entry(hid_link, &hdev->bpf.links[type], node) {
+                       hid_link->hdev = NULL; /* auto-detach link */
+               }
+       }
+       mutex_unlock(&bpf_hid_mutex);
+}
+EXPORT_SYMBOL_GPL(bpf_hid_exit);
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index b88688264ad0..d1c05011e5ab 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -3,6 +3,7 @@
  */
 #include <linux/bpf.h>
 #include <linux/bpf-cgroup.h>
+#include <linux/bpf-hid.h>
 #include <linux/bpf_trace.h>
 #include <linux/bpf_lirc.h>
 #include <linux/bpf_verifier.h>
@@ -2205,6 +2206,7 @@ static bool is_sys_admin_prog_type(enum bpf_prog_type prog_type)
 {
        switch (prog_type) {
        case BPF_PROG_TYPE_LIRC_MODE2:
+       case BPF_PROG_TYPE_HID:
                return true;
        default:
                return false;
@@ -3199,6 +3201,11 @@ attach_type_to_prog_type(enum bpf_attach_type attach_type)
                return BPF_PROG_TYPE_SK_LOOKUP;
        case BPF_XDP:
                return BPF_PROG_TYPE_XDP;
+       case BPF_HID_DEVICE_EVENT:
+       case BPF_HID_RDESC_FIXUP:
+       case BPF_HID_USER_EVENT:
+       case BPF_HID_DRIVER_EVENT:
+               return BPF_PROG_TYPE_HID;
        default:
                return BPF_PROG_TYPE_UNSPEC;
        }
@@ -3342,6 +3349,11 @@ static int bpf_prog_query(const union bpf_attr *attr,
        case BPF_SK_MSG_VERDICT:
        case BPF_SK_SKB_VERDICT:
                return sock_map_bpf_prog_query(attr, uattr);
+       case BPF_HID_DEVICE_EVENT:
+       case BPF_HID_RDESC_FIXUP:
+       case BPF_HID_USER_EVENT:
+       case BPF_HID_DRIVER_EVENT:
+               return bpf_hid_prog_query(attr, uattr);
        default:
                return -EINVAL;
        }
@@ -4336,6 +4348,8 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
                ret = bpf_perf_link_attach(attr, prog);
                break;
 #endif
+       case BPF_PROG_TYPE_HID:
+               return bpf_hid_link_create(attr, prog);
        default:
                ret = -EINVAL;
        }
--
2.35.1
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help