Hi,
And here comes the v5 of the HID-BPF series.
I managed to achive the same functionalities than v3 this time.
Handling per-device BPF program was "interesting" to say the least,
but I don't know if we can have a generic BPF way of handling such
situation.
The interesting bits is that now the BPF core changes are rather small,
and I am mostly using existing facilities.
I didn't managed to write selftests for the RET_PTR_TO_MEM kfunc,
because I can not call kmalloc while in a SEC("tc") program to match
what the other kfunc tests are doing.
And AFAICT, the most interesting bits would be to implement verifier
selftests, which are way out of my league, given that they are
implemented as plain bytecode.
The logic is the following (see also the last patch for some more
documentation):
- hid-bpf first preloads a BPF program in the kernel that does a few
things:
* find out which attach_btf_id are associated with our trace points
* adds a bpf_tail_call() BPF program that I can use to "call" any
other BPF program stored into a jump table
* monitors the releases of struct bpf_prog, and when there are no
other users than us, detach the bpf progs from the HID devices
- users then declare their tracepoints and then call
hid_bpf_attach_prog() in a SEC("syscall") program
- hid-bpf then calls multiple time the bpf_tail_call() program with a
different index in the jump table whenever there is an event coming
from a matching HID device
Note that I am tempted to pin an "attach_hid_program" in the bpffs so
that users don't need to declare one, but I am afraid this will be one
more API to handle, so maybe not.
I am also wondering if I should not strip out hid_bpf_jmp_table of most
of its features and implement everything as a BPF program. This might
remove the need to add the kernel light skeleton implementations of map
modifications, and might also possibly be more re-usable for other
subsystems. But every plan I do in my head involves a lot of back and
forth between the kernel and BPF to achieve the same, which doesn't feel
right. The tricky part is the RCU list of programs that is stored in each
device and also the global state of the jump table.
Anyway, something to look for in a next version if there is a push for it.
FWIW, patch 1 is something I'd like to get merged sooner. With 2
colleagues, we are also working on supporting the "revoke" functionality
of a fd for USB and for hidraw. While hidraw can be emulated with the
current features, we need the syscall kfuncs for USB, because when we
revoke a USB access, we also need to kick out the user, and for that, we
need to actually execute code in the kernel from a userspace event.
Anyway, happy reviewing.
Cheers,
Benjamin
[Patch series based on commit 68084a136420 ("selftests/bpf: Fix building bpf selftests statically")
in the bpf-next tree]
Benjamin Tissoires (17):
bpf/btf: also allow kfunc in tracing and syscall programs
bpf/verifier: allow kfunc to return an allocated mem
bpf: prepare for more bpf syscall to be used from kernel and user
space.
libbpf: add map_get_fd_by_id and map_delete_elem in light skeleton
HID: core: store the unique system identifier in hid_device
HID: export hid_report_type to uapi
HID: initial BPF implementation
selftests/bpf: add tests for the HID-bpf initial implementation
HID: bpf: allocate data memory for device_event BPF programs
selftests/bpf/hid: add test to change the report size
HID: bpf: introduce hid_hw_request()
selftests/bpf: add tests for bpf_hid_hw_request
HID: bpf: allow to change the report descriptor
selftests/bpf: add report descriptor fixup tests
samples/bpf: add new hid_mouse example
selftests/bpf: Add a test for BPF_F_INSERT_HEAD
Documentation: add HID-BPF docs
Documentation/hid/hid-bpf.rst | 528 ++++++++++
Documentation/hid/index.rst | 1 +
drivers/hid/Kconfig | 2 +
drivers/hid/Makefile | 2 +
drivers/hid/bpf/Kconfig | 19 +
drivers/hid/bpf/Makefile | 11 +
drivers/hid/bpf/entrypoints/Makefile | 88 ++
drivers/hid/bpf/entrypoints/README | 4 +
drivers/hid/bpf/entrypoints/entrypoints.bpf.c | 78 ++
.../hid/bpf/entrypoints/entrypoints.lskel.h | 782 ++++++++++++++
drivers/hid/bpf/hid_bpf_dispatch.c | 565 ++++++++++
drivers/hid/bpf/hid_bpf_dispatch.h | 28 +
drivers/hid/bpf/hid_bpf_jmp_table.c | 587 +++++++++++
drivers/hid/hid-core.c | 43 +-
include/linux/btf.h | 7 +
include/linux/hid.h | 29 +-
include/linux/hid_bpf.h | 144 +++
include/uapi/linux/hid.h | 12 +
include/uapi/linux/hid_bpf.h | 25 +
kernel/bpf/btf.c | 47 +-
kernel/bpf/syscall.c | 10 +-
kernel/bpf/verifier.c | 72 +-
samples/bpf/.gitignore | 1 +
samples/bpf/Makefile | 23 +
samples/bpf/hid_mouse.bpf.c | 134 +++
samples/bpf/hid_mouse.c | 157 +++
tools/lib/bpf/skel_internal.h | 23 +
tools/testing/selftests/bpf/config | 3 +
tools/testing/selftests/bpf/prog_tests/hid.c | 990 ++++++++++++++++++
tools/testing/selftests/bpf/progs/hid.c | 222 ++++
30 files changed, 4593 insertions(+), 44 deletions(-)
create mode 100644 Documentation/hid/hid-bpf.rst
create mode 100644 drivers/hid/bpf/Kconfig
create mode 100644 drivers/hid/bpf/Makefile
create mode 100644 drivers/hid/bpf/entrypoints/Makefile
create mode 100644 drivers/hid/bpf/entrypoints/README
create mode 100644 drivers/hid/bpf/entrypoints/entrypoints.bpf.c
create mode 100644 drivers/hid/bpf/entrypoints/entrypoints.lskel.h
create mode 100644 drivers/hid/bpf/hid_bpf_dispatch.c
create mode 100644 drivers/hid/bpf/hid_bpf_dispatch.h
create mode 100644 drivers/hid/bpf/hid_bpf_jmp_table.c
create mode 100644 include/linux/hid_bpf.h
create mode 100644 include/uapi/linux/hid_bpf.h
create mode 100644 samples/bpf/hid_mouse.bpf.c
create mode 100644 samples/bpf/hid_mouse.c
create mode 100644 tools/testing/selftests/bpf/prog_tests/hid.c
create mode 100644 tools/testing/selftests/bpf/progs/hid.c
--
2.36.1
From: Nikolay Borisov <nborisov(a)suse.com>
[ Upstream commit e299bcd4d16ff86f46c48df1062c8aae0eca1ed8 ]
Since commit 6521f8917082 ("namei: prepare for idmapped mounts")
vfs_link's prototype was changed, the kprobe definition in
profiler selftest in turn wasn't updated. The result is that all
argument after the first are now stored in different registers. This
means that self-test has been broken ever since. Fix it by updating the
kprobe definition accordingly.
Signed-off-by: Nikolay Borisov <nborisov(a)suse.com>
Signed-off-by: Andrii Nakryiko <andrii(a)kernel.org>
Link: https://lore.kernel.org/bpf/20220331140949.1410056-1-nborisov@suse.com
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
---
tools/testing/selftests/bpf/progs/profiler.inc.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/profiler.inc.h b/tools/testing/selftests/bpf/progs/profiler.inc.h
index 4896fdf816f7..92331053dba3 100644
--- a/tools/testing/selftests/bpf/progs/profiler.inc.h
+++ b/tools/testing/selftests/bpf/progs/profiler.inc.h
@@ -826,8 +826,9 @@ int kprobe_ret__do_filp_open(struct pt_regs* ctx)
SEC("kprobe/vfs_link")
int BPF_KPROBE(kprobe__vfs_link,
- struct dentry* old_dentry, struct inode* dir,
- struct dentry* new_dentry, struct inode** delegated_inode)
+ struct dentry* old_dentry, struct user_namespace *mnt_userns,
+ struct inode* dir, struct dentry* new_dentry,
+ struct inode** delegated_inode)
{
struct bpf_func_stats_ctx stats_ctx;
bpf_stats_enter(&stats_ctx, profiler_bpf_vfs_link);
--
2.35.1
From: Quentin Monnet <quentin(a)isovalent.com>
[ Upstream commit 4eeebce6ac4ad80ee8243bb847c98e0e55848d47 ]
The script for checking that various lists of types in bpftool remain in
sync with the UAPI BPF header uses a regex to parse enum bpf_prog_type.
If this enum contains a set of values different from the list of program
types in bpftool, it complains.
This script should have reported the addition, some time ago, of the new
BPF_PROG_TYPE_SYSCALL, which was not reported to bpftool's program types
list. It failed to do so, because it failed to parse that new type from
the enum. This is because the new value, in the BPF header, has an
explicative comment on the same line, and the regex does not support
that.
Let's update the script to support parsing enum values when they have
comments on the same line.
Signed-off-by: Quentin Monnet <quentin(a)isovalent.com>
Signed-off-by: Andrii Nakryiko <andrii(a)kernel.org>
Link: https://lore.kernel.org/bpf/20220404140944.64744-1-quentin@isovalent.com
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
---
tools/testing/selftests/bpf/test_bpftool_synctypes.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/test_bpftool_synctypes.py b/tools/testing/selftests/bpf/test_bpftool_synctypes.py
index be54b7335a76..5717db4e0862 100755
--- a/tools/testing/selftests/bpf/test_bpftool_synctypes.py
+++ b/tools/testing/selftests/bpf/test_bpftool_synctypes.py
@@ -172,7 +172,7 @@ class FileExtractor(object):
@enum_name: name of the enum to parse
"""
start_marker = re.compile(f'enum {enum_name} {{\n')
- pattern = re.compile('^\s*(BPF_\w+),?$')
+ pattern = re.compile('^\s*(BPF_\w+),?(\s+/\*.*\*/)?$')
end_marker = re.compile('^};')
parser = BlockParser(self.reader)
parser.search_block(start_marker)
--
2.35.1
From: Nikolay Borisov <nborisov(a)suse.com>
[ Upstream commit e299bcd4d16ff86f46c48df1062c8aae0eca1ed8 ]
Since commit 6521f8917082 ("namei: prepare for idmapped mounts")
vfs_link's prototype was changed, the kprobe definition in
profiler selftest in turn wasn't updated. The result is that all
argument after the first are now stored in different registers. This
means that self-test has been broken ever since. Fix it by updating the
kprobe definition accordingly.
Signed-off-by: Nikolay Borisov <nborisov(a)suse.com>
Signed-off-by: Andrii Nakryiko <andrii(a)kernel.org>
Link: https://lore.kernel.org/bpf/20220331140949.1410056-1-nborisov@suse.com
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
---
tools/testing/selftests/bpf/progs/profiler.inc.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/profiler.inc.h b/tools/testing/selftests/bpf/progs/profiler.inc.h
index 4896fdf816f7..92331053dba3 100644
--- a/tools/testing/selftests/bpf/progs/profiler.inc.h
+++ b/tools/testing/selftests/bpf/progs/profiler.inc.h
@@ -826,8 +826,9 @@ int kprobe_ret__do_filp_open(struct pt_regs* ctx)
SEC("kprobe/vfs_link")
int BPF_KPROBE(kprobe__vfs_link,
- struct dentry* old_dentry, struct inode* dir,
- struct dentry* new_dentry, struct inode** delegated_inode)
+ struct dentry* old_dentry, struct user_namespace *mnt_userns,
+ struct inode* dir, struct dentry* new_dentry,
+ struct inode** delegated_inode)
{
struct bpf_func_stats_ctx stats_ctx;
bpf_stats_enter(&stats_ctx, profiler_bpf_vfs_link);
--
2.35.1
From: Daniel Latypov <dlatypov(a)google.com>
[ Upstream commit dbf0b0d53a2b5afa6ef7372dcedf52302669fc2c ]
Consider this invocation
$ ./tools/testing/kunit/kunit.py parse <<EOF
TAP version 14
1..2
ok 1 - suite
# Subtest: no_tests_suite
# catastrophic error!
not ok 1 - no_tests_suite
EOF
It will have a 0 exit code even though there's a "not ok".
Consider this one:
$ ./tools/testing/kunit/kunit.py parse <<EOF
TAP version 14
1..2
ok 1 - suite
not ok 1 - no_tests_suite
EOF
It will a non-zero exit code.
Why?
We have this line in the kunit_parser.py
> parent_test = parse_test_header(lines, test)
where we have special handling when we see "# Subtest" and we ignore the
explicit reported "not ok 1" status!
Also, NO_TESTS at a suite-level only results in a non-zero status code
where then there's only one suite atm.
This change is the minimal one to make sure we don't overwrite it.
Signed-off-by: Daniel Latypov <dlatypov(a)google.com>
Reviewed-by: David Gow <davidgow(a)google.com>
Reviewed-by: Brendan Higgins <brendanhiggins(a)google.com>
Signed-off-by: Shuah Khan <skhan(a)linuxfoundation.org>
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
---
tools/testing/kunit/kunit_parser.py | 7 +++++--
.../test_data/test_is_test_passed-no_tests_no_plan.log | 2 +-
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py
index 05ff334761dd..2f93ed1d7f99 100644
--- a/tools/testing/kunit/kunit_parser.py
+++ b/tools/testing/kunit/kunit_parser.py
@@ -789,8 +789,11 @@ def parse_test(lines: LineStream, expected_num: int, log: List[str]) -> Test:
# Check for there being no tests
if parent_test and len(subtests) == 0:
- test.status = TestStatus.NO_TESTS
- test.add_error('0 tests run!')
+ # Don't override a bad status if this test had one reported.
+ # Assumption: no subtests means CRASHED is from Test.__init__()
+ if test.status in (TestStatus.TEST_CRASHED, TestStatus.SUCCESS):
+ test.status = TestStatus.NO_TESTS
+ test.add_error('0 tests run!')
# Add statuses to TestCounts attribute in Test object
bubble_up_test_results(test)
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-no_tests_no_plan.log b/tools/testing/kunit/test_data/test_is_test_passed-no_tests_no_plan.log
index dd873c981108..4f81876ee6f1 100644
--- a/tools/testing/kunit/test_data/test_is_test_passed-no_tests_no_plan.log
+++ b/tools/testing/kunit/test_data/test_is_test_passed-no_tests_no_plan.log
@@ -3,5 +3,5 @@ TAP version 14
# Subtest: suite
1..1
# Subtest: case
- ok 1 - case # SKIP
+ ok 1 - case
ok 1 - suite
--
2.35.1