2022-12-05 16:54:30

by Benjamin Tissoires

[permalink] [raw]
Subject: [PATCH HID for-next v2 0/4] HID: bpf: remove the need for ALLOW_ERROR_INJECTION and Kconfig fixes

Hi,

So this patch series aims at solving both [0] and [1].

The first one is bpf related and concerns the ALLOW_ERROR_INJECTION API.
It is considered as a hack to begin with, so introduce a proper kernel
API to declare when a BPF hook can have its return value changed.

The second one is related to the fact that
DYNAMIC_FTRACE_WITH_DIRECT_CALLS is currently not enabled on arm64, and
that means that the current HID-BPF implementation doesn't work there
for now.

The first patch actually touches the bpf core code, but it would be
easier if we could merge it through the hid tree in the for-6.2/hid-bpf
branch once we get the proper acks.

Cheers,
Benjamin

[0] https://lore.kernel.org/all/[email protected]/
[1] https://lore.kernel.org/r/CABRcYmKyRchQhabi1Vd9RcMQFCcb=EtWyEbFDFRTc-L-U8WhgA@mail.gmail.com

Benjamin Tissoires (4):
bpf: do not rely on ALLOW_ERROR_INJECTION for fmod_ret
HID: bpf: do not rely on ALLOW_ERROR_INJECTION
HID: bpf: enforce HID_BPF dependencies
selftests: hid: ensures we have the proper requirements in config

drivers/hid/bpf/Kconfig | 3 ++-
drivers/hid/bpf/hid_bpf_dispatch.c | 20 ++++++++++++--
drivers/hid/bpf/hid_bpf_jmp_table.c | 1 -
include/linux/btf.h | 3 +++
kernel/bpf/btf.c | 41 +++++++++++++++++++++++++++++
kernel/bpf/verifier.c | 17 ++++++++++--
net/bpf/test_run.c | 14 +++++++---
tools/testing/selftests/hid/config | 1 +
8 files changed, 91 insertions(+), 9 deletions(-)

--
2.38.1


2022-12-05 16:54:31

by Benjamin Tissoires

[permalink] [raw]
Subject: [PATCH HID for-next v2 2/4] HID: bpf: do not rely on ALLOW_ERROR_INJECTION

Now that we have aproper non debug API to declare which function is
fmodret, we can rely on it.

Link: https://lore.kernel.org/all/[email protected]/
Suggested-by: Alexei Starovoitov <[email protected]>
Signed-off-by: Benjamin Tissoires <[email protected]>
---
drivers/hid/bpf/hid_bpf_dispatch.c | 20 ++++++++++++++++++--
drivers/hid/bpf/hid_bpf_jmp_table.c | 1 -
2 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
index c3920c7964dc..58e608ebf0fa 100644
--- a/drivers/hid/bpf/hid_bpf_dispatch.c
+++ b/drivers/hid/bpf/hid_bpf_dispatch.c
@@ -44,7 +44,6 @@ __weak noinline int hid_bpf_device_event(struct hid_bpf_ctx *ctx)
{
return 0;
}
-ALLOW_ERROR_INJECTION(hid_bpf_device_event, ERRNO);

u8 *
dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data,
@@ -105,7 +104,6 @@ __weak noinline int hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx)
{
return 0;
}
-ALLOW_ERROR_INJECTION(hid_bpf_rdesc_fixup, ERRNO);

u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size)
{
@@ -429,6 +427,18 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
return ret;
}

+/* our HID-BPF entrypoints */
+BTF_SET8_START(hid_bpf_fmodret_ids)
+BTF_ID_FLAGS(func, hid_bpf_device_event)
+BTF_ID_FLAGS(func, hid_bpf_rdesc_fixup)
+BTF_ID_FLAGS(func, __hid_bpf_tail_call)
+BTF_SET8_END(hid_bpf_fmodret_ids)
+
+static const struct btf_kfunc_id_set hid_bpf_fmodret_set = {
+ .owner = THIS_MODULE,
+ .set = &hid_bpf_fmodret_ids,
+};
+
/* for syscall HID-BPF */
BTF_SET8_START(hid_bpf_syscall_kfunc_ids)
BTF_ID_FLAGS(func, hid_bpf_attach_prog)
@@ -495,6 +505,12 @@ static int __init hid_bpf_init(void)
* will not be available, so nobody will be able to use the functionality.
*/

+ err = register_btf_fmodret_id_set(&hid_bpf_fmodret_set);
+ if (err) {
+ pr_warn("error while registering fmodret entrypoints: %d", err);
+ return 0;
+ }
+
err = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &hid_bpf_kfunc_set);
if (err) {
pr_warn("error while setting HID BPF tracing kfuncs: %d", err);
diff --git a/drivers/hid/bpf/hid_bpf_jmp_table.c b/drivers/hid/bpf/hid_bpf_jmp_table.c
index 579a6c06906e..207972b028d9 100644
--- a/drivers/hid/bpf/hid_bpf_jmp_table.c
+++ b/drivers/hid/bpf/hid_bpf_jmp_table.c
@@ -103,7 +103,6 @@ __weak noinline int __hid_bpf_tail_call(struct hid_bpf_ctx *ctx)
{
return 0;
}
-ALLOW_ERROR_INJECTION(__hid_bpf_tail_call, ERRNO);

int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type,
struct hid_bpf_ctx_kern *ctx_kern)
--
2.38.1

2022-12-05 16:54:45

by Benjamin Tissoires

[permalink] [raw]
Subject: [PATCH HID for-next v2 3/4] HID: bpf: enforce HID_BPF dependencies

As mentioned in the link below, having JIT and BPF is not enough to
have fentry/fexit/fmod_ret APIs. This resolves the error that
happens on a system without tracing enabled when hid-bpf tries to
load itself.

Link: https://lore.kernel.org/r/CABRcYmKyRchQhabi1Vd9RcMQFCcb=EtWyEbFDFRTc-L-U8WhgA@mail.gmail.com
Fixes: f5c27da4e3c8 ("HID: initial BPF implementation")
Signed-off-by: Benjamin Tissoires <[email protected]>
---
drivers/hid/bpf/Kconfig | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/bpf/Kconfig b/drivers/hid/bpf/Kconfig
index 298634fc3335..03f52145b83b 100644
--- a/drivers/hid/bpf/Kconfig
+++ b/drivers/hid/bpf/Kconfig
@@ -4,7 +4,8 @@ menu "HID-BPF support"
config HID_BPF
bool "HID-BPF support"
default HID_SUPPORT
- depends on BPF && BPF_SYSCALL
+ depends on BPF && BPF_SYSCALL && \
+ DYNAMIC_FTRACE_WITH_DIRECT_CALLS
help
This option allows to support eBPF programs on the HID subsystem.
eBPF programs can fix HID devices in a lighter way than a full
--
2.38.1

2022-12-05 16:54:50

by Benjamin Tissoires

[permalink] [raw]
Subject: [PATCH HID for-next v2 4/4] selftests: hid: ensures we have the proper requirements in config

DYNAMIC_FTRACE_WITH_DIRECT_CALLS is implicit on x86_64 but is still a
WIP on aarm64. Ensure we get it selected to not have any surprises.

Signed-off-by: Benjamin Tissoires <[email protected]>
---
tools/testing/selftests/hid/config | 1 +
1 file changed, 1 insertion(+)

diff --git a/tools/testing/selftests/hid/config b/tools/testing/selftests/hid/config
index d4130489c1b1..9c5a55abca6b 100644
--- a/tools/testing/selftests/hid/config
+++ b/tools/testing/selftests/hid/config
@@ -11,6 +11,7 @@ CONFIG_BPF_SYSCALL=y
CONFIG_BPF=y
CONFIG_CGROUP_BPF=y
CONFIG_DEBUG_INFO_BTF=y
+CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS=y
CONFIG_FPROBE=y
CONFIG_FTRACE_SYSCALLS=y
CONFIG_FUNCTION_TRACER=y
--
2.38.1

2022-12-05 17:01:33

by Benjamin Tissoires

[permalink] [raw]
Subject: [PATCH HID for-next v2 1/4] bpf: do not rely on ALLOW_ERROR_INJECTION for fmod_ret

The current way of expressing that a non-bpf kernel component is willing
to accept that bpf programs can be attached to it and that they can change
the return value is to abuse ALLOW_ERROR_INJECTION.
This is debated in the link below, and the result is that it is not a
reasonable thing to do.

Reuse the kfunc declaration structure to also tag the kernel functions
we want to be fmodret. This way we can control from any subsystem which
functions are being modified by bpf without touching the verifier.


Link: https://lore.kernel.org/all/[email protected]/
Suggested-by: Alexei Starovoitov <[email protected]>
Signed-off-by: Benjamin Tissoires <[email protected]>
---
include/linux/btf.h | 3 +++
kernel/bpf/btf.c | 41 +++++++++++++++++++++++++++++++++++++++++
kernel/bpf/verifier.c | 17 +++++++++++++++--
net/bpf/test_run.c | 14 +++++++++++---
4 files changed, 70 insertions(+), 5 deletions(-)

diff --git a/include/linux/btf.h b/include/linux/btf.h
index f9aababc5d78..f71cfb20b9bf 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -412,8 +412,11 @@ struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog);
u32 *btf_kfunc_id_set_contains(const struct btf *btf,
enum bpf_prog_type prog_type,
u32 kfunc_btf_id);
+u32 *btf_kern_func_is_modify_return(const struct btf *btf,
+ u32 kfunc_btf_id);
int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
const struct btf_kfunc_id_set *s);
+int register_btf_fmodret_id_set(const struct btf_kfunc_id_set *kset);
s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id);
int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt,
struct module *owner);
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 35c07afac924..a22f3f45aca3 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -204,6 +204,7 @@ enum btf_kfunc_hook {
BTF_KFUNC_HOOK_STRUCT_OPS,
BTF_KFUNC_HOOK_TRACING,
BTF_KFUNC_HOOK_SYSCALL,
+ BTF_KFUNC_HOOK_FMODRET,
BTF_KFUNC_HOOK_MAX,
};

@@ -7448,6 +7449,19 @@ u32 *btf_kfunc_id_set_contains(const struct btf *btf,
return __btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
}

+/* Caution:
+ * Reference to the module (obtained using btf_try_get_module) corresponding to
+ * the struct btf *MUST* be held when calling this function from verifier
+ * context. This is usually true as we stash references in prog's kfunc_btf_tab;
+ * keeping the reference for the duration of the call provides the necessary
+ * protection for looking up a well-formed btf->kfunc_set_tab.
+ */
+u32 *btf_kern_func_is_modify_return(const struct btf *btf,
+ u32 kfunc_btf_id)
+{
+ return __btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id);
+}
+
/* This function must be invoked only from initcalls/module init functions */
int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
const struct btf_kfunc_id_set *kset)
@@ -7478,6 +7492,33 @@ int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
}
EXPORT_SYMBOL_GPL(register_btf_kfunc_id_set);

+/* This function must be invoked only from initcalls/module init functions */
+int register_btf_fmodret_id_set(const struct btf_kfunc_id_set *kset)
+{
+ struct btf *btf;
+ int ret;
+
+ btf = btf_get_module_btf(kset->owner);
+ if (!btf) {
+ if (!kset->owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) {
+ pr_err("missing vmlinux BTF, cannot register kfuncs\n");
+ return -ENOENT;
+ }
+ if (kset->owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)) {
+ pr_err("missing module BTF, cannot register kfuncs\n");
+ return -ENOENT;
+ }
+ return 0;
+ }
+ if (IS_ERR(btf))
+ return PTR_ERR(btf);
+
+ ret = btf_populate_kfunc_set(btf, BTF_KFUNC_HOOK_FMODRET, kset->set);
+ btf_put(btf);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(register_btf_fmodret_id_set);
+
s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id)
{
struct btf_id_dtor_kfunc_tab *tab = btf->dtor_kfunc_tab;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 225666307bba..0525972de998 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -15021,12 +15021,22 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
ret = -EINVAL;
switch (prog->type) {
case BPF_PROG_TYPE_TRACING:
- /* fentry/fexit/fmod_ret progs can be sleepable only if they are
+
+ /* fentry/fexit/fmod_ret progs can be sleepable if they are
* attached to ALLOW_ERROR_INJECTION and are not in denylist.
*/
if (!check_non_sleepable_error_inject(btf_id) &&
within_error_injection_list(addr))
ret = 0;
+ /* fentry/fexit/fmod_ret progs can also be sleepable if they are
+ * in the fmodret id set with the KF_SLEEPABLE flag.
+ */
+ else {
+ u32 *flags = btf_kern_func_is_modify_return(btf, btf_id);
+
+ if (flags && (*flags & KF_SLEEPABLE))
+ ret = 0;
+ }
break;
case BPF_PROG_TYPE_LSM:
/* LSM progs check that they are attached to bpf_lsm_*() funcs.
@@ -15047,7 +15057,10 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
bpf_log(log, "can't modify return codes of BPF programs\n");
return -EINVAL;
}
- ret = check_attach_modify_return(addr, tname);
+ ret = -EINVAL;
+ if (btf_kern_func_is_modify_return(btf, btf_id) ||
+ !check_attach_modify_return(addr, tname))
+ ret = 0;
if (ret) {
bpf_log(log, "%s() is not modifiable\n", tname);
return ret;
diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c
index 13d578ce2a09..5079fb089b5f 100644
--- a/net/bpf/test_run.c
+++ b/net/bpf/test_run.c
@@ -489,7 +489,6 @@ int noinline bpf_fentry_test1(int a)
return a + 1;
}
EXPORT_SYMBOL_GPL(bpf_fentry_test1);
-ALLOW_ERROR_INJECTION(bpf_fentry_test1, ERRNO);

int noinline bpf_fentry_test2(int a, u64 b)
{
@@ -733,7 +732,15 @@ noinline void bpf_kfunc_call_test_destructive(void)

__diag_pop();

-ALLOW_ERROR_INJECTION(bpf_modify_return_test, ERRNO);
+BTF_SET8_START(bpf_test_modify_return_ids)
+BTF_ID_FLAGS(func, bpf_modify_return_test)
+BTF_ID_FLAGS(func, bpf_fentry_test1, KF_SLEEPABLE)
+BTF_SET8_END(bpf_test_modify_return_ids)
+
+static const struct btf_kfunc_id_set bpf_test_modify_return_set = {
+ .owner = THIS_MODULE,
+ .set = &bpf_test_modify_return_ids,
+};

BTF_SET8_START(test_sk_check_kfunc_ids)
BTF_ID_FLAGS(func, bpf_kfunc_call_test1)
@@ -1668,7 +1675,8 @@ static int __init bpf_prog_test_run_init(void)
};
int ret;

- ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_prog_test_kfunc_set);
+ ret = register_btf_fmodret_id_set(&bpf_test_modify_return_set);
+ ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SCHED_CLS, &bpf_prog_test_kfunc_set);
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &bpf_prog_test_kfunc_set);
ret = ret ?: register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &bpf_prog_test_kfunc_set);
return ret ?: register_btf_id_dtor_kfuncs(bpf_prog_test_dtor_kfunc,
--
2.38.1

2022-12-05 22:12:07

by Alexei Starovoitov

[permalink] [raw]
Subject: Re: [PATCH HID for-next v2 1/4] bpf: do not rely on ALLOW_ERROR_INJECTION for fmod_ret

On Mon, Dec 05, 2022 at 05:48:53PM +0100, Benjamin Tissoires wrote:
> The current way of expressing that a non-bpf kernel component is willing
> to accept that bpf programs can be attached to it and that they can change
> the return value is to abuse ALLOW_ERROR_INJECTION.
> This is debated in the link below, and the result is that it is not a
> reasonable thing to do.
>
> Reuse the kfunc declaration structure to also tag the kernel functions
> we want to be fmodret. This way we can control from any subsystem which
> functions are being modified by bpf without touching the verifier.
>
>
> Link: https://lore.kernel.org/all/[email protected]/
> Suggested-by: Alexei Starovoitov <[email protected]>
> Signed-off-by: Benjamin Tissoires <[email protected]>
> ---
> include/linux/btf.h | 3 +++
> kernel/bpf/btf.c | 41 +++++++++++++++++++++++++++++++++++++++++
> kernel/bpf/verifier.c | 17 +++++++++++++++--
> net/bpf/test_run.c | 14 +++++++++++---
> 4 files changed, 70 insertions(+), 5 deletions(-)
>
> diff --git a/include/linux/btf.h b/include/linux/btf.h
> index f9aababc5d78..f71cfb20b9bf 100644
> --- a/include/linux/btf.h
> +++ b/include/linux/btf.h
> @@ -412,8 +412,11 @@ struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog);
> u32 *btf_kfunc_id_set_contains(const struct btf *btf,
> enum bpf_prog_type prog_type,
> u32 kfunc_btf_id);
> +u32 *btf_kern_func_is_modify_return(const struct btf *btf,
> + u32 kfunc_btf_id);
> int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
> const struct btf_kfunc_id_set *s);
> +int register_btf_fmodret_id_set(const struct btf_kfunc_id_set *kset);
> s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id);
> int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt,
> struct module *owner);
> diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
> index 35c07afac924..a22f3f45aca3 100644
> --- a/kernel/bpf/btf.c
> +++ b/kernel/bpf/btf.c
> @@ -204,6 +204,7 @@ enum btf_kfunc_hook {
> BTF_KFUNC_HOOK_STRUCT_OPS,
> BTF_KFUNC_HOOK_TRACING,
> BTF_KFUNC_HOOK_SYSCALL,
> + BTF_KFUNC_HOOK_FMODRET,
> BTF_KFUNC_HOOK_MAX,
> };
>
> @@ -7448,6 +7449,19 @@ u32 *btf_kfunc_id_set_contains(const struct btf *btf,
> return __btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
> }
>
> +/* Caution:
> + * Reference to the module (obtained using btf_try_get_module) corresponding to
> + * the struct btf *MUST* be held when calling this function from verifier
> + * context. This is usually true as we stash references in prog's kfunc_btf_tab;
> + * keeping the reference for the duration of the call provides the necessary
> + * protection for looking up a well-formed btf->kfunc_set_tab.
> + */

There is no need to copy paste that comment from btf_kfunc_id_set_contains().
One place is enough.

> +u32 *btf_kern_func_is_modify_return(const struct btf *btf,
> + u32 kfunc_btf_id)

How about btf_kfunc_is_modify_return ?
For consistency.

> +{
> + return __btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id);
> +}
> +
> /* This function must be invoked only from initcalls/module init functions */
> int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
> const struct btf_kfunc_id_set *kset)
> @@ -7478,6 +7492,33 @@ int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
> }
> EXPORT_SYMBOL_GPL(register_btf_kfunc_id_set);
>
> +/* This function must be invoked only from initcalls/module init functions */
> +int register_btf_fmodret_id_set(const struct btf_kfunc_id_set *kset)
> +{
> + struct btf *btf;
> + int ret;
> +
> + btf = btf_get_module_btf(kset->owner);
> + if (!btf) {
> + if (!kset->owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) {
> + pr_err("missing vmlinux BTF, cannot register kfuncs\n");
> + return -ENOENT;
> + }
> + if (kset->owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)) {
> + pr_err("missing module BTF, cannot register kfuncs\n");
> + return -ENOENT;
> + }
> + return 0;
> + }
> + if (IS_ERR(btf))
> + return PTR_ERR(btf);
> +
> + ret = btf_populate_kfunc_set(btf, BTF_KFUNC_HOOK_FMODRET, kset->set);
> + btf_put(btf);
> + return ret;
> +}

This is a bit too much copy-paste from register_btf_kfunc_id_set().
Please share the code. Like:

int __register_btf_kfunc_id_set(enum btf_kfunc_hook hook, const struct btf_kfunc_id_set *kset)
{
...
}

int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
const struct btf_kfunc_id_set *kset)
{
hook = bpf_prog_type_to_kfunc_hook(prog_type);
return __register_btf_kfunc_id_set(hook, kset);
}

int register_btf_fmodret_id_set(const struct btf_kfunc_id_set *kset)
{
return __register_btf_kfunc_id_set(BTF_KFUNC_HOOK_FMODRET, kset);
}