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

From: Song Liu
Date: Fri Mar 18 2022 - 16:48:41 EST


On Fri, Mar 18, 2022 at 9:16 AM Benjamin Tissoires
<benjamin.tissoires@xxxxxxxxxx> wrote:
>
[...]
>
> 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) */
> +};
> +
[...]

> 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.

> 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,
> };
[...]
> +
> /* 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**.

> */
> #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?

> +
> + 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
>