| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/20 22:07 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The patch only modifies BPF selftests (tools/testing/selftests/bpf/) by converting an old-style test into the test_progs framework. It does not make any functional changes to the kernel code itself.",
"WorthFuzzing": false
} |
| 1/1 | 2026/08/20 22:07 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit c1cc0a8fe35e6708fce0a18193e4689531a3aac3\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Aug 20 22:07:31 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore\nindex 986a6389186ba..b815bf0d88774 100644\n--- a/tools/testing/selftests/bpf/.gitignore\n+++ b/tools/testing/selftests/bpf/.gitignore\n@@ -17,7 +17,6 @@ test_verifier_log\n feature\n urandom_read\n test_sockmap\n-test_lirc_mode2_user\n flow_dissector_load\n test_tcpnotify_user\n test_libbpf\ndiff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile\nindex 2b2f93dec474b..f8ec0f574559a 100644\n--- a/tools/testing/selftests/bpf/Makefile\n+++ b/tools/testing/selftests/bpf/Makefile\n@@ -123,7 +123,6 @@ TEST_FILES = xsk_prereqs.sh $(wildcard progs/btf_dump_test_case_*.c)\n \n # Order correspond to 'make run_tests' order\n TEST_PROGS := test_kmod.sh \\\n-\ttest_lirc_mode2.sh \\\n \ttest_bpftool_build.sh \\\n \ttest_doc_build.sh \\\n \ttest_xsk.sh \\\n@@ -141,7 +140,6 @@ TEST_GEN_PROGS_EXTENDED = \\\n \tbench \\\n \tflow_dissector_load \\\n \ttest_cpp \\\n-\ttest_lirc_mode2_user \\\n \tveristat \\\n \txdp_features \\\n \txdp_hw_metadata \\\n@@ -338,7 +336,6 @@ $(OUTPUT)/test_sockmap: $(CGROUP_HELPERS) $(TESTING_HELPERS)\n $(OUTPUT)/test_tcpnotify_user: $(CGROUP_HELPERS) $(TESTING_HELPERS) $(TRACE_HELPERS)\n $(OUTPUT)/test_sock_fields: $(CGROUP_HELPERS) $(TESTING_HELPERS)\n $(OUTPUT)/test_tag: $(TESTING_HELPERS)\n-$(OUTPUT)/test_lirc_mode2_user: $(TESTING_HELPERS)\n $(OUTPUT)/flow_dissector_load: $(TESTING_HELPERS)\n $(OUTPUT)/test_maps: $(TESTING_HELPERS)\n $(OUTPUT)/test_verifier: $(TESTING_HELPERS) $(CAP_HELPERS) $(UNPRIV_HELPERS)\ndiff --git a/tools/testing/selftests/bpf/prog_tests/lirc_mode2.c b/tools/testing/selftests/bpf/prog_tests/lirc_mode2.c\nnew file mode 100644\nindex 0000000000000..8e485db70b78a\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/prog_tests/lirc_mode2.c\n@@ -0,0 +1,334 @@\n+// SPDX-License-Identifier: GPL-2.0\n+// test ir decoder\n+//\n+// Copyright (C) 2018 Sean Young \u003csean@mess.org\u003e\n+\n+// A lirc chardev is a device representing a consumer IR (cir) device which\n+// can receive infrared signals from remote control and/or transmit IR.\n+//\n+// IR is sent as a series of pulses and space somewhat like morse code. The\n+// BPF program can decode this into scancodes so that rc-core can translate\n+// this into input key codes using the rc keymap.\n+//\n+// This test works by sending IR over rc-loopback, so the IR is processed by\n+// BPF and then decoded into scancodes. The lirc chardev must be the one\n+// associated with rc-loopback, see the output of ir-keytable(1).\n+//\n+// The following CONFIG options must be enabled for the test to succeed:\n+// CONFIG_RC_CORE=y\n+// CONFIG_BPF_RAWIR_EVENT=y\n+// CONFIG_RC_LOOPBACK=y\n+// CONFIG_LIRC=y\n+\n+#include \u003clinux/input.h\u003e\n+#include \u003clinux/lirc.h\u003e\n+#include \u003cglob.h\u003e\n+#include \u003climits.h\u003e\n+#include \u003cpoll.h\u003e\n+#include \u003ctest_progs.h\u003e\n+#include \"lirc_mode2.skel.h\"\n+\n+/*\n+ * Read the DEVNAME= line out of the first uevent file that matches\n+ * pattern, and turn it into a /dev/\u003cname\u003e path.\n+ */\n+static bool find_devname(const char *pattern, char *path, size_t path_sz)\n+{\n+\tglob_t gl = {};\n+\tbool found = false;\n+\tFILE *f;\n+\n+\tif (glob(pattern, 0, NULL, \u0026gl) || gl.gl_pathc == 0)\n+\t\tgoto out;\n+\n+\tf = fopen(gl.gl_pathv[0], \"r\");\n+\tif (!f)\n+\t\tgoto out;\n+\n+\tchar line[256];\n+\n+\twhile (fgets(line, sizeof(line), f)) {\n+\t\tchar *val;\n+\n+\t\tif (strncmp(line, \"DEVNAME=\", 8))\n+\t\t\tcontinue;\n+\n+\t\tval = line + 8;\n+\t\tval[strcspn(val, \"\\n\")] = '\\0';\n+\t\tsnprintf(path, path_sz, \"/dev/%s\", val);\n+\t\tfound = true;\n+\t\tbreak;\n+\t}\n+\n+\tfclose(f);\n+out:\n+\tglobfree(\u0026gl);\n+\treturn found;\n+}\n+\n+/* Load rc-loopback and find the lirc and input chardevs it created. */\n+static bool find_loopback_devices(char *lirc_path, char *input_path,\n+\t\t\t\t size_t path_sz)\n+{\n+\tglob_t gl = {};\n+\tbool found = false;\n+\n+\t/* Ignore failure, we check for the resulting devices below. */\n+\tSYS_NOFAIL(\"modprobe rc-loopback \u003e /dev/null 2\u003e\u00261\");\n+\n+\tif (glob(\"/sys/class/rc/rc*\", 0, NULL, \u0026gl))\n+\t\tgoto out;\n+\n+\tfor (size_t i = 0; i \u003c gl.gl_pathc; i++) {\n+\t\tconst char *rcdir = gl.gl_pathv[i];\n+\t\tchar uevent_path[PATH_MAX];\n+\t\tchar uevent[4096];\n+\t\tchar pattern[PATH_MAX];\n+\t\tFILE *f;\n+\t\tsize_t n;\n+\n+\t\tsnprintf(uevent_path, sizeof(uevent_path), \"%s/uevent\", rcdir);\n+\t\tf = fopen(uevent_path, \"r\");\n+\t\tif (!f)\n+\t\t\tcontinue;\n+\t\tn = fread(uevent, 1, sizeof(uevent) - 1, f);\n+\t\tfclose(f);\n+\t\tuevent[n] = '\\0';\n+\n+\t\tif (!strstr(uevent, \"DRV_NAME=rc-loopback\"))\n+\t\t\tcontinue;\n+\n+\t\tsnprintf(pattern, sizeof(pattern), \"%s/lirc*/uevent\", rcdir);\n+\t\tif (!find_devname(pattern, lirc_path, path_sz))\n+\t\t\tcontinue;\n+\n+\t\tsnprintf(pattern, sizeof(pattern), \"%s/input*/event*/uevent\", rcdir);\n+\t\tif (!find_devname(pattern, input_path, path_sz))\n+\t\t\tcontinue;\n+\n+\t\tfound = true;\n+\t\tbreak;\n+\t}\n+\n+out:\n+\tif (!found) {\n+\t\tfprintf(stderr, \"No rc devices found\\n\");\n+\t\tfprintf(stderr, \"Enable CONFIG_RC_LOOPBACK, CONFIG_RC_LOOPBACK, and CONFIG_LIRC\\n\");\n+?\t}\n+\n+\tglobfree(\u0026gl);\n+\treturn found;\n+}\n+\n+void test_lirc_mode2(void)\n+{\n+\tchar lirc_path[PATH_MAX], input_path[PATH_MAX];\n+\tint lircfd = -1, inputfd = -1, progfd, progfd2 = -1;\n+\tstruct lirc_mode2 *skel = NULL, *skel2 = NULL;\n+\t__u32 prog_ids[10], prog_flags[10], prog_cnt;\n+\tstruct bpf_prog_info info;\n+\t__u32 info_len, prog_id, prog_id2;\n+\tint testir1 = 0x8ead;\t/* keydown flag (0x8000) | scancode 0xead */\n+\tint testir2 = 0x4081;\t/* pointer_rel flag (0x4000) | rel_x=1 | rel_y=1 */\n+\tstruct input_event event;\n+\tstruct pollfd pfd = {};\n+\tint ret;\n+\n+\tif (getuid() != 0) {\n+\t\ttest__skip();\n+\t\treturn;\n+\t}\n+\n+\tif (!find_loopback_devices(lirc_path, input_path, sizeof(lirc_path))) {\n+\t\ttest__skip();\n+\t\treturn;\n+\t}\n+\n+\tskel = lirc_mode2__open_and_load();\n+\tif (!ASSERT_OK_PTR(skel, \"lirc_mode2__open_and_load\"))\n+\t\treturn;\n+\n+\tprogfd = bpf_program__fd(skel-\u003eprogs.bpf_decoder);\n+\n+\tmemset(\u0026info, 0, sizeof(info));\n+\tinfo_len = sizeof(info);\n+\tret = bpf_prog_get_info_by_fd(progfd, \u0026info, \u0026info_len);\n+\tif (!ASSERT_OK(ret, \"get first program's info\"))\n+\t\tgoto out;\n+\tprog_id = info.id;\n+\n+\tlircfd = open(lirc_path, O_RDWR | O_NONBLOCK);\n+\tif (!ASSERT_GE(lircfd, 0, \"open lirc device\"))\n+\t\tgoto out;\n+\n+\t/* Try to detach it before it was ever attached, should fail. */\n+\tret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2);\n+\tif (!ASSERT_EQ(ret, -ENOENT, \"detach unattached program\"))\n+\t\tgoto out;\n+\n+\tinputfd = open(input_path, O_RDONLY | O_NONBLOCK);\n+\tif (!ASSERT_GE(inputfd, 0, \"open input device\"))\n+\t\tgoto out;\n+\n+\tprog_cnt = ARRAY_SIZE(prog_ids);\n+\tret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,\n+\t\t\t \u0026prog_cnt);\n+\tif (!ASSERT_OK(ret, \"query programs before attach\"))\n+\t\tgoto out;\n+\tif (!ASSERT_EQ(prog_cnt, 0, \"no programs should be attached yet\"))\n+\t\tgoto out;\n+\n+\t/* Invalid attach flags must be rejected, and must not attach. */\n+\tret = bpf_prog_attach(progfd, lircfd, BPF_LIRC_MODE2, 1);\n+\tif (!ASSERT_EQ(ret, -EINVAL, \"attach with invalid flags\"))\n+\t\tgoto out;\n+\n+\tprog_cnt = ARRAY_SIZE(prog_ids);\n+\tret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,\n+\t\t\t \u0026prog_cnt);\n+\tif (!ASSERT_OK(ret, \"query programs after rejected attach\"))\n+\t\tgoto out;\n+\tif (!ASSERT_EQ(prog_cnt, 0, \"rejected attach should not attach\"))\n+\t\tgoto out;\n+\n+\tret = bpf_prog_attach(progfd, lircfd, BPF_LIRC_MODE2, 0);\n+\tif (!ASSERT_OK(ret, \"attach program to lirc device\"))\n+\t\tgoto out;\n+\n+\t/* Invalid query flags must be rejected too, without upsetting state. */\n+\tprog_cnt = ARRAY_SIZE(prog_ids);\n+\tret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 1, prog_flags, prog_ids,\n+\t\t\t \u0026prog_cnt);\n+\tASSERT_EQ(ret, -EINVAL, \"query with invalid flags\");\n+\n+\tprog_cnt = ARRAY_SIZE(prog_ids);\n+\tret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,\n+\t\t\t \u0026prog_cnt);\n+\tif (!ASSERT_OK(ret, \"query programs after attach\"))\n+\t\tgoto out_detach;\n+\tif (!ASSERT_EQ(prog_cnt, 1, \"one program should be attached\"))\n+\t\tgoto out_detach;\n+\tASSERT_EQ(prog_ids[0], prog_id, \"queried id should match attached program\");\n+\n+\t/* Write raw IR */\n+\tret = write(lircfd, \u0026testir1, sizeof(testir1));\n+\tif (!ASSERT_EQ(ret, sizeof(testir1), \"send test IR message 1\"))\n+\t\tgoto out_detach;\n+\n+\tpfd.fd = inputfd;\n+\tpfd.events = POLLIN;\n+\n+\tfor (;;) {\n+\t\tpoll(\u0026pfd, 1, 100);\n+\n+\t\t/* Read decoded IR */\n+\t\tret = read(inputfd, \u0026event, sizeof(event));\n+\t\tif (!ASSERT_EQ(ret, sizeof(event), \"read decoded IR 1\"))\n+\t\t\tgoto out_detach;\n+\n+\t\tif (event.type == EV_MSC \u0026\u0026 event.code == MSC_SCAN \u0026\u0026\n+\t\t event.value == 0xead)\n+\t\t\tbreak;\n+\t}\n+\n+\t/* Write raw IR */\n+\tret = write(lircfd, \u0026testir2, sizeof(testir2));\n+\tif (!ASSERT_EQ(ret, sizeof(testir2), \"send test IR message 2\"))\n+\t\tgoto out_detach;\n+\n+\tfor (;;) {\n+\t\tpoll(\u0026pfd, 1, 100);\n+\n+\t\t/* Read decoded IR */\n+\t\tret = read(inputfd, \u0026event, sizeof(event));\n+\t\tif (!ASSERT_EQ(ret, sizeof(event), \"read decoded IR 2\"))\n+\t\t\tgoto out_detach;\n+\n+\t\tif (event.type == EV_REL \u0026\u0026 event.code == REL_Y \u0026\u0026\n+\t\t event.value == 1)\n+\t\t\tbreak;\n+\t}\n+\n+\tprog_cnt = ARRAY_SIZE(prog_ids);\n+\tret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,\n+\t\t\t \u0026prog_cnt);\n+\tif (!ASSERT_OK(ret, \"query programs after IR was decoded\"))\n+\t\tgoto out_detach;\n+\tif (!ASSERT_EQ(prog_cnt, 1, \"one program should still be attached\"))\n+\t\tgoto out_detach;\n+\n+\t/*\n+\t * The lirc chardev can hold more than one attached program at once.\n+\t * Load a second, independent instance and check it can be attached\n+\t * alongside the first, queried, and then detached on its own\n+\t * without disturbing the first program's attachment.\n+\t */\n+\tskel2 = lirc_mode2__open_and_load();\n+\tif (!ASSERT_OK_PTR(skel2, \"lirc_mode2__open_and_load (2nd)\"))\n+\t\tgoto out_detach;\n+\n+\tprogfd2 = bpf_program__fd(skel2-\u003eprogs.bpf_decoder);\n+\n+\tmemset(\u0026info, 0, sizeof(info));\n+\tinfo_len = sizeof(info);\n+\tret = bpf_prog_get_info_by_fd(progfd2, \u0026info, \u0026info_len);\n+\tif (!ASSERT_OK(ret, \"get second program's info\"))\n+\t\tgoto out_detach;\n+\tprog_id2 = info.id;\n+\n+\tret = bpf_prog_attach(progfd2, lircfd, BPF_LIRC_MODE2, 0);\n+\tif (!ASSERT_OK(ret, \"attach second program to lirc device\"))\n+\t\tgoto out_detach;\n+\n+\tprog_cnt = ARRAY_SIZE(prog_ids);\n+\tret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,\n+\t\t\t \u0026prog_cnt);\n+\tif (!ASSERT_OK(ret, \"query programs after second attach\"))\n+\t\tgoto out_detach2;\n+\tif (!ASSERT_EQ(prog_cnt, 2, \"two programs should be attached\"))\n+\t\tgoto out_detach2;\n+\tASSERT_TRUE((prog_ids[0] == prog_id \u0026\u0026 prog_ids[1] == prog_id2) ||\n+\t\t (prog_ids[0] == prog_id2 \u0026\u0026 prog_ids[1] == prog_id),\n+\t\t \"queried ids should be the two attached programs\");\n+\n+\t/* Detach the second program; the first should remain attached. */\n+\tret = bpf_prog_detach2(progfd2, lircfd, BPF_LIRC_MODE2);\n+\tif (!ASSERT_OK(ret, \"detach second program\"))\n+\t\tgoto out_detach2;\n+\n+\t/* Detaching an already-detached program should now fail. */\n+\tret = bpf_prog_detach2(progfd2, lircfd, BPF_LIRC_MODE2);\n+\tASSERT_EQ(ret, -ENOENT, \"detach second program again\");\n+\n+\tprog_cnt = ARRAY_SIZE(prog_ids);\n+\tret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,\n+\t\t\t \u0026prog_cnt);\n+\tif (!ASSERT_OK(ret, \"query programs after second detach\"))\n+\t\tgoto out_detach;\n+\tif (!ASSERT_EQ(prog_cnt, 1, \"one program should remain attached\"))\n+\t\tgoto out_detach;\n+\tASSERT_EQ(prog_ids[0], prog_id, \"remaining program should be the first one\");\n+\n+out_detach:\n+\t/* Let's try detaching it now it is actually attached. */\n+\tret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2);\n+\tASSERT_OK(ret, \"detach program from lirc device\");\n+\n+\t/* Detaching it again should now fail the same way. */\n+\tret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2);\n+\tASSERT_EQ(ret, -ENOENT, \"detach program from lirc device again\");\n+\tgoto out;\n+\n+out_detach2:\n+\t/* Best-effort cleanup of the second program before bailing out. */\n+\tbpf_prog_detach2(progfd2, lircfd, BPF_LIRC_MODE2);\n+\tgoto out_detach;\n+\n+out:\n+\tif (inputfd \u003e= 0)\n+\t\tclose(inputfd);\n+\tif (lircfd \u003e= 0)\n+\t\tclose(lircfd);\n+\tlirc_mode2__destroy(skel2);\n+\tlirc_mode2__destroy(skel);\n+}\ndiff --git a/tools/testing/selftests/bpf/progs/lirc_mode2.c b/tools/testing/selftests/bpf/progs/lirc_mode2.c\nnew file mode 100644\nindex 0000000000000..98137f3c5c03d\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/lirc_mode2.c\n@@ -0,0 +1,32 @@\n+// SPDX-License-Identifier: GPL-2.0\n+// test ir decoder\n+//\n+// Copyright (C) 2018 Sean Young \u003csean@mess.org\u003e\n+\n+#include \u003clinux/bpf.h\u003e\n+#include \u003clinux/lirc.h\u003e\n+#include \u003cbpf/bpf_helpers.h\u003e\n+\n+SEC(\"lirc_mode2\")\n+int bpf_decoder(unsigned int *sample)\n+{\n+\tif (LIRC_IS_PULSE(*sample)) {\n+\t\tunsigned int duration = LIRC_VALUE(*sample);\n+\n+\t\t/*\n+\t\t * Flag bits picked deliberately low: rc-loopback simulates\n+\t\t * a receiver overflow for any pulse over MS_TO_US(50) (see\n+\t\t * loop_tx_ir() in rc-loopback.c), which would silently\n+\t\t * swallow the sample before it ever reaches this decoder.\n+\t\t */\n+\t\tif (duration \u0026 0x8000)\n+\t\t\tbpf_rc_keydown(sample, 0x40, duration \u0026 0x3fff, 0);\n+\t\tif (duration \u0026 0x4000)\n+\t\t\tbpf_rc_pointer_rel(sample, (duration \u003e\u003e 7) \u0026 0x7f,\n+\t\t\t\t\t duration \u0026 0x7f);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+char _license[] SEC(\"license\") = \"GPL\";\ndiff --git a/tools/testing/selftests/bpf/progs/test_lirc_mode2_kern.c b/tools/testing/selftests/bpf/progs/test_lirc_mode2_kern.c\ndeleted file mode 100644\nindex cbe4284c032fe..0000000000000\n--- a/tools/testing/selftests/bpf/progs/test_lirc_mode2_kern.c\n+++ /dev/null\n@@ -1,26 +0,0 @@\n-// SPDX-License-Identifier: GPL-2.0\n-// test ir decoder\n-//\n-// Copyright (C) 2018 Sean Young \u003csean@mess.org\u003e\n-\n-#include \u003clinux/bpf.h\u003e\n-#include \u003clinux/lirc.h\u003e\n-#include \u003cbpf/bpf_helpers.h\u003e\n-\n-SEC(\"lirc_mode2\")\n-int bpf_decoder(unsigned int *sample)\n-{\n-\tif (LIRC_IS_PULSE(*sample)) {\n-\t\tunsigned int duration = LIRC_VALUE(*sample);\n-\n-\t\tif (duration \u0026 0x1000)\n-\t\t\tbpf_rc_keydown(sample, 0x40, duration \u0026 0xffff, 0);\n-\t\tif (duration \u0026 0x2000)\n-\t\t\tbpf_rc_pointer_rel(sample, (duration \u003e\u003e 8) \u0026 0xff,\n-\t\t\t\t\t duration \u0026 0xff);\n-\t}\n-\n-\treturn 0;\n-}\n-\n-char _license[] SEC(\"license\") = \"GPL\";\ndiff --git a/tools/testing/selftests/bpf/test_lirc_mode2.sh b/tools/testing/selftests/bpf/test_lirc_mode2.sh\ndeleted file mode 100755\nindex 5252b91f48a18..0000000000000\n--- a/tools/testing/selftests/bpf/test_lirc_mode2.sh\n+++ /dev/null\n@@ -1,41 +0,0 @@\n-#!/bin/bash\n-# SPDX-License-Identifier: GPL-2.0\n-\n-# Kselftest framework requirement - SKIP code is 4.\n-ksft_skip=4\n-ret=$ksft_skip\n-\n-msg=\"skip all tests:\"\n-if [ $UID != 0 ]; then\n-\techo $msg please run this as root \u003e\u00262\n-\texit $ksft_skip\n-fi\n-\n-GREEN='\\033[0;92m'\n-RED='\\033[0;31m'\n-NC='\\033[0m' # No Color\n-\n-modprobe rc-loopback\n-\n-for i in /sys/class/rc/rc*\n-do\n-\tif grep -q DRV_NAME=rc-loopback $i/uevent\n-\tthen\n-\t\tLIRCDEV=$(grep DEVNAME= $i/lirc*/uevent | sed sQDEVNAME=Q/dev/Q)\n-\t\tINPUTDEV=$(grep DEVNAME= $i/input*/event*/uevent | sed sQDEVNAME=Q/dev/Q)\n-\tfi\n-done\n-\n-if [ -n \"$LIRCDEV\" ];\n-then\n-\tTYPE=lirc_mode2\n-\t./test_lirc_mode2_user $LIRCDEV $INPUTDEV\n-\tret=$?\n-\tif [ $ret -ne 0 ]; then\n-\t\techo -e ${RED}\"FAIL: $TYPE\"${NC}\n-\telse\n-\t\techo -e ${GREEN}\"PASS: $TYPE\"${NC}\n-\tfi\n-fi\n-\n-exit $ret\ndiff --git a/tools/testing/selftests/bpf/test_lirc_mode2_user.c b/tools/testing/selftests/bpf/test_lirc_mode2_user.c\ndeleted file mode 100644\nindex cd191da20d143..0000000000000\n--- a/tools/testing/selftests/bpf/test_lirc_mode2_user.c\n+++ /dev/null\n@@ -1,177 +0,0 @@\n-// SPDX-License-Identifier: GPL-2.0\n-// test ir decoder\n-//\n-// Copyright (C) 2018 Sean Young \u003csean@mess.org\u003e\n-\n-// A lirc chardev is a device representing a consumer IR (cir) device which\n-// can receive infrared signals from remote control and/or transmit IR.\n-//\n-// IR is sent as a series of pulses and space somewhat like morse code. The\n-// BPF program can decode this into scancodes so that rc-core can translate\n-// this into input key codes using the rc keymap.\n-//\n-// This test works by sending IR over rc-loopback, so the IR is processed by\n-// BPF and then decoded into scancodes. The lirc chardev must be the one\n-// associated with rc-loopback, see the output of ir-keytable(1).\n-//\n-// The following CONFIG options must be enabled for the test to succeed:\n-// CONFIG_RC_CORE=y\n-// CONFIG_BPF_RAWIR_EVENT=y\n-// CONFIG_RC_LOOPBACK=y\n-\n-// Steps:\n-// 1. Open the /dev/lircN device for rc-loopback (given on command line)\n-// 2. Attach bpf_lirc_mode2 program which decodes some IR.\n-// 3. Send some IR to the same IR device; since it is loopback, this will\n-// end up in the bpf program\n-// 4. bpf program should decode IR and report keycode\n-// 5. We can read keycode from same /dev/lirc device\n-\n-#include \u003clinux/bpf.h\u003e\n-#include \u003clinux/input.h\u003e\n-#include \u003cerrno.h\u003e\n-#include \u003cstdio.h\u003e\n-#include \u003cstdlib.h\u003e\n-#include \u003cstring.h\u003e\n-#include \u003cunistd.h\u003e\n-#include \u003cpoll.h\u003e\n-#include \u003csys/types.h\u003e\n-#include \u003csys/ioctl.h\u003e\n-#include \u003csys/stat.h\u003e\n-#include \u003cfcntl.h\u003e\n-\n-#include \"bpf_util.h\"\n-#include \u003cbpf/bpf.h\u003e\n-#include \u003cbpf/libbpf.h\u003e\n-\n-#include \"testing_helpers.h\"\n-\n-int main(int argc, char **argv)\n-{\n-\tstruct bpf_object *obj;\n-\tint ret, lircfd, progfd, inputfd;\n-\tint testir1 = 0x1ead;\n-\tint testir2 = 0x2101;\n-\tu32 prog_ids[10], prog_flags[10], prog_cnt;\n-\n-\tif (argc != 3) {\n-\t\tprintf(\"Usage: %s /dev/lircN /dev/input/eventM\\n\", argv[0]);\n-\t\treturn 2;\n-\t}\n-\n-\tret = bpf_prog_test_load(\"test_lirc_mode2_kern.bpf.o\",\n-\t\t\t\t BPF_PROG_TYPE_LIRC_MODE2, \u0026obj, \u0026progfd);\n-\tif (ret) {\n-\t\tprintf(\"Failed to load bpf program\\n\");\n-\t\treturn 1;\n-\t}\n-\n-\tlircfd = open(argv[1], O_RDWR | O_NONBLOCK);\n-\tif (lircfd == -1) {\n-\t\tprintf(\"failed to open lirc device %s: %m\\n\", argv[1]);\n-\t\treturn 1;\n-\t}\n-\n-\t/* Let's try detach it before it was ever attached */\n-\tret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2);\n-\tif (ret != -ENOENT) {\n-\t\tprintf(\"bpf_prog_detach2 not attached should fail: %m\\n\");\n-\t\treturn 1;\n-\t}\n-\n-\tinputfd = open(argv[2], O_RDONLY | O_NONBLOCK);\n-\tif (inputfd == -1) {\n-\t\tprintf(\"failed to open input device %s: %m\\n\", argv[1]);\n-\t\treturn 1;\n-\t}\n-\n-\tprog_cnt = 10;\n-\tret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,\n-\t\t\t \u0026prog_cnt);\n-\tif (ret) {\n-\t\tprintf(\"Failed to query bpf programs on lirc device: %m\\n\");\n-\t\treturn 1;\n-\t}\n-\n-\tif (prog_cnt != 0) {\n-\t\tprintf(\"Expected nothing to be attached\\n\");\n-\t\treturn 1;\n-\t}\n-\n-\tret = bpf_prog_attach(progfd, lircfd, BPF_LIRC_MODE2, 0);\n-\tif (ret) {\n-\t\tprintf(\"Failed to attach bpf to lirc device: %m\\n\");\n-\t\treturn 1;\n-\t}\n-\n-\t/* Write raw IR */\n-\tret = write(lircfd, \u0026testir1, sizeof(testir1));\n-\tif (ret != sizeof(testir1)) {\n-\t\tprintf(\"Failed to send test IR message: %m\\n\");\n-\t\treturn 1;\n-\t}\n-\n-\tstruct pollfd pfd = { .fd = inputfd, .events = POLLIN };\n-\tstruct input_event event;\n-\n-\tfor (;;) {\n-\t\tpoll(\u0026pfd, 1, 100);\n-\n-\t\t/* Read decoded IR */\n-\t\tret = read(inputfd, \u0026event, sizeof(event));\n-\t\tif (ret != sizeof(event)) {\n-\t\t\tprintf(\"Failed to read decoded IR: %m\\n\");\n-\t\t\treturn 1;\n-\t\t}\n-\n-\t\tif (event.type == EV_MSC \u0026\u0026 event.code == MSC_SCAN \u0026\u0026\n-\t\t event.value == 0x1ead) {\n-\t\t\tbreak;\n-\t\t}\n-\t}\n-\n-\t/* Write raw IR */\n-\tret = write(lircfd, \u0026testir2, sizeof(testir2));\n-\tif (ret != sizeof(testir2)) {\n-\t\tprintf(\"Failed to send test IR message: %m\\n\");\n-\t\treturn 1;\n-\t}\n-\n-\tfor (;;) {\n-\t\tpoll(\u0026pfd, 1, 100);\n-\n-\t\t/* Read decoded IR */\n-\t\tret = read(inputfd, \u0026event, sizeof(event));\n-\t\tif (ret != sizeof(event)) {\n-\t\t\tprintf(\"Failed to read decoded IR: %m\\n\");\n-\t\t\treturn 1;\n-\t\t}\n-\n-\t\tif (event.type == EV_REL \u0026\u0026 event.code == REL_Y \u0026\u0026\n-\t\t event.value == 1 ) {\n-\t\t\tbreak;\n-\t\t}\n-\t}\n-\n-\tprog_cnt = 10;\n-\tret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,\n-\t\t\t \u0026prog_cnt);\n-\tif (ret) {\n-\t\tprintf(\"Failed to query bpf programs on lirc device: %m\\n\");\n-\t\treturn 1;\n-\t}\n-\n-\tif (prog_cnt != 1) {\n-\t\tprintf(\"Expected one program to be attached\\n\");\n-\t\treturn 1;\n-\t}\n-\n-\t/* Let's try detaching it now it is actually attached */\n-\tret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2);\n-\tif (ret) {\n-\t\tprintf(\"bpf_prog_detach2: returned %m\\n\");\n-\t\treturn 1;\n-\t}\n-\n-\treturn 0;\n-}\n"
} |
| 2/1 | 2026/08/20 22:07 | agent | patch-evaluator |
0mModel:best-expensive Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies BPF selftests (tools/testing/selftests/bpf/) by converting an old-style test into the test_progs framework. It does not make any functional changes to the kernel code itself.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit c1cc0a8fe35e6708fce0a18193e4689531a3aac3
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Aug 20 22:07:31 2026 +0000
syz-cluster: applied patch under review
diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore
index 986a6389186ba..b815bf0d88774 100644
--- a/tools/testing/selftests/bpf/.gitignore
+++ b/tools/testing/selftests/bpf/.gitignore
@@ -17,7 +17,6 @@ test_verifier_log
feature
urandom_read
test_sockmap
-test_lirc_mode2_user
flow_dissector_load
test_tcpnotify_user
test_libbpf
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 2b2f93dec474b..f8ec0f574559a 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -123,7 +123,6 @@ TEST_FILES = xsk_prereqs.sh $(wildcard progs/btf_dump_test_case_*.c)
# Order correspond to 'make run_tests' order
TEST_PROGS := test_kmod.sh \
- test_lirc_mode2.sh \
test_bpftool_build.sh \
test_doc_build.sh \
test_xsk.sh \
@@ -141,7 +140,6 @@ TEST_GEN_PROGS_EXTENDED = \
bench \
flow_dissector_load \
test_cpp \
- test_lirc_mode2_user \
veristat \
xdp_features \
xdp_hw_metadata \
@@ -338,7 +336,6 @@ $(OUTPUT)/test_sockmap: $(CGROUP_HELPERS) $(TESTING_HELPERS)
$(OUTPUT)/test_tcpnotify_user: $(CGROUP_HELPERS) $(TESTING_HELPERS) $(TRACE_HELPERS)
$(OUTPUT)/test_sock_fields: $(CGROUP_HELPERS) $(TESTING_HELPERS)
$(OUTPUT)/test_tag: $(TESTING_HELPERS)
-$(OUTPUT)/test_lirc_mode2_user: $(TESTING_HELPERS)
$(OUTPUT)/flow_dissector_load: $(TESTING_HELPERS)
$(OUTPUT)/test_maps: $(TESTING_HELPERS)
$(OUTPUT)/test_verifier: $(TESTING_HELPERS) $(CAP_HELPERS) $(UNPRIV_HELPERS)
diff --git a/tools/testing/selftests/bpf/prog_tests/lirc_mode2.c b/tools/testing/selftests/bpf/prog_tests/lirc_mode2.c
new file mode 100644
index 0000000000000..8e485db70b78a
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/lirc_mode2.c
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0
+// test ir decoder
+//
+// Copyright (C) 2018 Sean Young <sean@mess.org>
+
+// A lirc chardev is a device representing a consumer IR (cir) device which
+// can receive infrared signals from remote control and/or transmit IR.
+//
+// IR is sent as a series of pulses and space somewhat like morse code. The
+// BPF program can decode this into scancodes so that rc-core can translate
+// this into input key codes using the rc keymap.
+//
+// This test works by sending IR over rc-loopback, so the IR is processed by
+// BPF and then decoded into scancodes. The lirc chardev must be the one
+// associated with rc-loopback, see the output of ir-keytable(1).
+//
+// The following CONFIG options must be enabled for the test to succeed:
+// CONFIG_RC_CORE=y
+// CONFIG_BPF_RAWIR_EVENT=y
+// CONFIG_RC_LOOPBACK=y
+// CONFIG_LIRC=y
+
+#include <linux/input.h>
+#include <linux/lirc.h>
+#include <glob.h>
+#include <limits.h>
+#include <poll.h>
+#include <test_progs.h>
+#include "lirc_mode2.skel.h"
+
+/*
+ * Read the DEVNAME= line out of the first uevent file that matches
+ * pattern, and turn it into a /dev/<name> path.
+ */
+static bool find_devname(const char *pattern, char *path, size_t path_sz)
+{
+ glob_t gl = {};
+ bool found = false;
+ FILE *f;
+
+ if (glob(pattern, 0, NULL, &gl) || gl.gl_pathc == 0)
+ goto out;
+
+ f = fopen(gl.gl_pathv[0], "r");
+ if (!f)
+ goto out;
+
+ char line[256];
+
+ while (fgets(line, sizeof(line), f)) {
+ char *val;
+
+ if (strncmp(line, "DEVNAME=", 8))
+ continue;
+
+ val = line + 8;
+ val[strcspn(val, "\n")] = '\0';
+ snprintf(path, path_sz, "/dev/%s", val);
+ found = true;
+ break;
+ }
+
+ fclose(f);
+out:
+ globfree(&gl);
+ return found;
+}
+
+/* Load rc-loopback and find the lirc and input chardevs it created. */
+static bool find_loopback_devices(char *lirc_path, char *input_path,
+ size_t path_sz)
+{
+ glob_t gl = {};
+ bool found = false;
+
+ /* Ignore failure, we check for the resulting devices below. */
+ SYS_NOFAIL("modprobe rc-loopback > /dev/null 2>&1");
+
+ if (glob("/sys/class/rc/rc*", 0, NULL, &gl))
+ goto out;
+
+ for (size_t i = 0; i < gl.gl_pathc; i++) {
+ const char *rcdir = gl.gl_pathv[i];
+ char uevent_path[PATH_MAX];
+ char uevent[4096];
+ char pattern[PATH_MAX];
+ FILE *f;
+ size_t n;
+
+ snprintf(uevent_path, sizeof(uevent_path), "%s/uevent", rcdir);
+ f = fopen(uevent_path, "r");
+ if (!f)
+ continue;
+ n = fread(uevent, 1, sizeof(uevent) - 1, f);
+ fclose(f);
+ uevent[n] = '\0';
+
+ if (!strstr(uevent, "DRV_NAME=rc-loopback"))
+ continue;
+
+ snprintf(pattern, sizeof(pattern), "%s/lirc*/uevent", rcdir);
+ if (!find_devname(pattern, lirc_path, path_sz))
+ continue;
+
+ snprintf(pattern, sizeof(pattern), "%s/input*/event*/uevent", rcdir);
+ if (!find_devname(pattern, input_path, path_sz))
+ continue;
+
+ found = true;
+ break;
+ }
+
+out:
+ if (!found) {
+ fprintf(stderr, "No rc devices found\n");
+ fprintf(stderr, "Enable CONFIG_RC_LOOPBACK, CONFIG_RC_LOOPBACK, and CONFIG_LIRC\n");
+? }
+
+ globfree(&gl);
+ return found;
+}
+
+void test_lirc_mode2(void)
+{
+ char lirc_path[PATH_MAX], input_path[PATH_MAX];
+ int lircfd = -1, inputfd = -1, progfd, progfd2 = -1;
+ struct lirc_mode2 *skel = NULL, *skel2 = NULL;
+ __u32 prog_ids[10], prog_flags[10], prog_cnt;
+ struct bpf_prog_info info;
+ __u32 info_len, prog_id, prog_id2;
+ int testir1 = 0x8ead; /* keydown flag (0x8000) | scancode 0xead */
+ int testir2 = 0x4081; /* pointer_rel flag (0x4000) | rel_x=1 | rel_y=1 */
+ struct input_event event;
+ struct pollfd pfd = {};
+ int ret;
+
+ if (getuid() != 0) {
+ test__skip();
+ return;
+ }
+
+ if (!find_loopback_devices(lirc_path, input_path, sizeof(lirc_path))) {
+ test__skip();
+ return;
+ }
+
+ skel = lirc_mode2__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "lirc_mode2__open_and_load"))
+ return;
+
+ progfd = bpf_program__fd(skel->progs.bpf_decoder);
+
+ memset(&info, 0, sizeof(info));
+ info_len = sizeof(info);
+ ret = bpf_prog_get_info_by_fd(progfd, &info, &info_len);
+ if (!ASSERT_OK(ret, "get first program's info"))
+ goto out;
+ prog_id = info.id;
+
+ lircfd = open(lirc_path, O_RDWR | O_NONBLOCK);
+ if (!ASSERT_GE(lircfd, 0, "open lirc device"))
+ goto out;
+
+ /* Try to detach it before it was ever attached, should fail. */
+ ret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2);
+ if (!ASSERT_EQ(ret, -ENOENT, "detach unattached program"))
+ goto out;
+
+ inputfd = open(input_path, O_RDONLY | O_NONBLOCK);
+ if (!ASSERT_GE(inputfd, 0, "open input device"))
+ goto out;
+
+ prog_cnt = ARRAY_SIZE(prog_ids);
+ ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,
+ &prog_cnt);
+ if (!ASSERT_OK(ret, "query programs before attach"))
+ goto out;
+ if (!ASSERT_EQ(prog_cnt, 0, "no programs should be attached yet"))
+ goto out;
+
+ /* Invalid attach flags must be rejected, and must not attach. */
+ ret = bpf_prog_attach(progfd, lircfd, BPF_LIRC_MODE2, 1);
+ if (!ASSERT_EQ(ret, -EINVAL, "attach with invalid flags"))
+ goto out;
+
+ prog_cnt = ARRAY_SIZE(prog_ids);
+ ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,
+ &prog_cnt);
+ if (!ASSERT_OK(ret, "query programs after rejected attach"))
+ goto out;
+ if (!ASSERT_EQ(prog_cnt, 0, "rejected attach should not attach"))
+ goto out;
+
+ ret = bpf_prog_attach(progfd, lircfd, BPF_LIRC_MODE2, 0);
+ if (!ASSERT_OK(ret, "attach program to lirc device"))
+ goto out;
+
+ /* Invalid query flags must be rejected too, without upsetting state. */
+ prog_cnt = ARRAY_SIZE(prog_ids);
+ ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 1, prog_flags, prog_ids,
+ &prog_cnt);
+ ASSERT_EQ(ret, -EINVAL, "query with invalid flags");
+
+ prog_cnt = ARRAY_SIZE(prog_ids);
+ ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,
+ &prog_cnt);
+ if (!ASSERT_OK(ret, "query programs after attach"))
+ goto out_detach;
+ if (!ASSERT_EQ(prog_cnt, 1, "one program should be attached"))
+ goto out_detach;
+ ASSERT_EQ(prog_ids[0], prog_id, "queried id should match attached program");
+
+ /* Write raw IR */
+ ret = write(lircfd, &testir1, sizeof(testir1));
+ if (!ASSERT_EQ(ret, sizeof(testir1), "send test IR message 1"))
+ goto out_detach;
+
+ pfd.fd = inputfd;
+ pfd.events = POLLIN;
+
+ for (;;) {
+ poll(&pfd, 1, 100);
+
+ /* Read decoded IR */
+ ret = read(inputfd, &event, sizeof(event));
+ if (!ASSERT_EQ(ret, sizeof(event), "read decoded IR 1"))
+ goto out_detach;
+
+ if (event.type == EV_MSC && event.code == MSC_SCAN &&
+ event.value == 0xead)
+ break;
+ }
+
+ /* Write raw IR */
+ ret = write(lircfd, &testir2, sizeof(testir2));
+ if (!ASSERT_EQ(ret, sizeof(testir2), "send test IR message 2"))
+ goto out_detach;
+
+ for (;;) {
+ poll(&pfd, 1, 100);
+
+ /* Read decoded IR */
+ ret = read(inputfd, &event, sizeof(event));
+ if (!ASSERT_EQ(ret, sizeof(event), "read decoded IR 2"))
+ goto out_detach;
+
+ if (event.type == EV_REL && event.code == REL_Y &&
+ event.value == 1)
+ break;
+ }
+
+ prog_cnt = ARRAY_SIZE(prog_ids);
+ ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,
+ &prog_cnt);
+ if (!ASSERT_OK(ret, "query programs after IR was decoded"))
+ goto out_detach;
+ if (!ASSERT_EQ(prog_cnt, 1, "one program should still be attached"))
+ goto out_detach;
+
+ /*
+ * The lirc chardev can hold more than one attached program at once.
+ * Load a second, independent instance and check it can be attached
+ * alongside the first, queried, and then detached on its own
+ * without disturbing the first program's attachment.
+ */
+ skel2 = lirc_mode2__open_and_load();
+ if (!ASSERT_OK_PTR(skel2, "lirc_mode2__open_and_load (2nd)"))
+ goto out_detach;
+
+ progfd2 = bpf_program__fd(skel2->progs.bpf_decoder);
+
+ memset(&info, 0, sizeof(info));
+ info_len = sizeof(info);
+ ret = bpf_prog_get_info_by_fd(progfd2, &info, &info_len);
+ if (!ASSERT_OK(ret, "get second program's info"))
+ goto out_detach;
+ prog_id2 = info.id;
+
+ ret = bpf_prog_attach(progfd2, lircfd, BPF_LIRC_MODE2, 0);
+ if (!ASSERT_OK(ret, "attach second program to lirc device"))
+ goto out_detach;
+
+ prog_cnt = ARRAY_SIZE(prog_ids);
+ ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,
+ &prog_cnt);
+ if (!ASSERT_OK(ret, "query programs after second attach"))
+ goto out_detach2;
+ if (!ASSERT_EQ(prog_cnt, 2, "two programs should be attached"))
+ goto out_detach2;
+ ASSERT_TRUE((prog_ids[0] == prog_id && prog_ids[1] == prog_id2) ||
+ (prog_ids[0] == prog_id2 && prog_ids[1] == prog_id),
+ "queried ids should be the two attached programs");
+
+ /* Detach the second program; the first should remain attached. */
+ ret = bpf_prog_detach2(progfd2, lircfd, BPF_LIRC_MODE2);
+ if (!ASSERT_OK(ret, "detach second program"))
+ goto out_detach2;
+
+ /* Detaching an already-detached program should now fail. */
+ ret = bpf_prog_detach2(progfd2, lircfd, BPF_LIRC_MODE2);
+ ASSERT_EQ(ret, -ENOENT, "detach second program again");
+
+ prog_cnt = ARRAY_SIZE(prog_ids);
+ ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,
+ &prog_cnt);
+ if (!ASSERT_OK(ret, "query programs after second detach"))
+ goto out_detach;
+ if (!ASSERT_EQ(prog_cnt, 1, "one program should remain attached"))
+ goto out_detach;
+ ASSERT_EQ(prog_ids[0], prog_id, "remaining program should be the first one");
+
+out_detach:
+ /* Let's try detaching it now it is actually attached. */
+ ret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2);
+ ASSERT_OK(ret, "detach program from lirc device");
+
+ /* Detaching it again should now fail the same way. */
+ ret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2);
+ ASSERT_EQ(ret, -ENOENT, "detach program from lirc device again");
+ goto out;
+
+out_detach2:
+ /* Best-effort cleanup of the second program before bailing out. */
+ bpf_prog_detach2(progfd2, lircfd, BPF_LIRC_MODE2);
+ goto out_detach;
+
+out:
+ if (inputfd >= 0)
+ close(inputfd);
+ if (lircfd >= 0)
+ close(lircfd);
+ lirc_mode2__destroy(skel2);
+ lirc_mode2__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/lirc_mode2.c b/tools/testing/selftests/bpf/progs/lirc_mode2.c
new file mode 100644
index 0000000000000..98137f3c5c03d
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/lirc_mode2.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0
+// test ir decoder
+//
+// Copyright (C) 2018 Sean Young <sean@mess.org>
+
+#include <linux/bpf.h>
+#include <linux/lirc.h>
+#include <bpf/bpf_helpers.h>
+
+SEC("lirc_mode2")
+int bpf_decoder(unsigned int *sample)
+{
+ if (LIRC_IS_PULSE(*sample)) {
+ unsigned int duration = LIRC_VALUE(*sample);
+
+ /*
+ * Flag bits picked deliberately low: rc-loopback simulates
+ * a receiver overflow for any pulse over MS_TO_US(50) (see
+ * loop_tx_ir() in rc-loopback.c), which would silently
+ * swallow the sample before it ever reaches this decoder.
+ */
+ if (duration & 0x8000)
+ bpf_rc_keydown(sample, 0x40, duration & 0x3fff, 0);
+ if (duration & 0x4000)
+ bpf_rc_pointer_rel(sample, (duration >> 7) & 0x7f,
+ duration & 0x7f);
+ }
+
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/test_lirc_mode2_kern.c b/tools/testing/selftests/bpf/progs/test_lirc_mode2_kern.c
deleted file mode 100644
index cbe4284c032fe..0000000000000
--- a/tools/testing/selftests/bpf/progs/test_lirc_mode2_kern.c
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-// test ir decoder
-//
-// Copyright (C) 2018 Sean Young <sean@mess.org>
-
-#include <linux/bpf.h>
-#include <linux/lirc.h>
-#include <bpf/bpf_helpers.h>
-
-SEC("lirc_mode2")
-int bpf_decoder(unsigned int *sample)
-{
- if (LIRC_IS_PULSE(*sample)) {
- unsigned int duration = LIRC_VALUE(*sample);
-
- if (duration & 0x1000)
- bpf_rc_keydown(sample, 0x40, duration & 0xffff, 0);
- if (duration & 0x2000)
- bpf_rc_pointer_rel(sample, (duration >> 8) & 0xff,
- duration & 0xff);
- }
-
- return 0;
-}
-
-char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_lirc_mode2.sh b/tools/testing/selftests/bpf/test_lirc_mode2.sh
deleted file mode 100755
index 5252b91f48a18..0000000000000
--- a/tools/testing/selftests/bpf/test_lirc_mode2.sh
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0
-
-# Kselftest framework requirement - SKIP code is 4.
-ksft_skip=4
-ret=$ksft_skip
-
-msg="skip all tests:"
-if [ $UID != 0 ]; then
- echo $msg please run this as root >&2
- exit $ksft_skip
-fi
-
-GREEN='\033[0;92m'
-RED='\033[0;31m'
-NC='\033[0m' # No Color
-
-modprobe rc-loopback
-
-for i in /sys/class/rc/rc*
-do
- if grep -q DRV_NAME=rc-loopback $i/uevent
- then
- LIRCDEV=$(grep DEVNAME= $i/lirc*/uevent | sed sQDEVNAME=Q/dev/Q)
- INPUTDEV=$(grep DEVNAME= $i/input*/event*/uevent | sed sQDEVNAME=Q/dev/Q)
- fi
-done
-
-if [ -n "$LIRCDEV" ];
-then
- TYPE=lirc_mode2
- ./test_lirc_mode2_user $LIRCDEV $INPUTDEV
- ret=$?
- if [ $ret -ne 0 ]; then
- echo -e ${RED}"FAIL: $TYPE"${NC}
- else
- echo -e ${GREEN}"PASS: $TYPE"${NC}
- fi
-fi
-
-exit $ret
diff --git a/tools/testing/selftests/bpf/test_lirc_mode2_user.c b/tools/testing/selftests/bpf/test_lirc_mode2_user.c
deleted file mode 100644
index cd191da20d143..0000000000000
--- a/tools/testing/selftests/bpf/test_lirc_mode2_user.c
+++ /dev/null
@@ -1,177 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-// test ir decoder
-//
-// Copyright (C) 2018 Sean Young <sean@mess.org>
-
-// A lirc chardev is a device representing a consumer IR (cir) device which
-// can receive infrared signals from remote control and/or transmit IR.
-//
-// IR is sent as a series of pulses and space somewhat like morse code. The
-// BPF program can decode this into scancodes so that rc-core can translate
-// this into input key codes using the rc keymap.
-//
-// This test works by sending IR over rc-loopback, so the IR is processed by
-// BPF and then decoded into scancodes. The lirc chardev must be the one
-// associated with rc-loopback, see the output of ir-keytable(1).
-//
-// The following CONFIG options must be enabled for the test to succeed:
-// CONFIG_RC_CORE=y
-// CONFIG_BPF_RAWIR_EVENT=y
-// CONFIG_RC_LOOPBACK=y
-
-// Steps:
-// 1. Open the /dev/lircN device for rc-loopback (given on command line)
-// 2. Attach bpf_lirc_mode2 program which decodes some IR.
-// 3. Send some IR to the same IR device; since it is loopback, this will
-// end up in the bpf program
-// 4. bpf program should decode IR and report keycode
-// 5. We can read keycode from same /dev/lirc device
-
-#include <linux/bpf.h>
-#include <linux/input.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <poll.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include "bpf_util.h"
-#include <bpf/bpf.h>
-#include <bpf/libbpf.h>
-
-#include "testing_helpers.h"
-
-int main(int argc, char **argv)
-{
- struct bpf_object *obj;
- int ret, lircfd, progfd, inputfd;
- int testir1 = 0x1ead;
- int testir2 = 0x2101;
- u32 prog_ids[10], prog_flags[10], prog_cnt;
-
- if (argc != 3) {
- printf("Usage: %s /dev/lircN /dev/input/eventM\n", argv[0]);
- return 2;
- }
-
- ret = bpf_prog_test_load("test_lirc_mode2_kern.bpf.o",
- BPF_PROG_TYPE_LIRC_MODE2, &obj, &progfd);
- if (ret) {
- printf("Failed to load bpf program\n");
- return 1;
- }
-
- lircfd = open(argv[1], O_RDWR | O_NONBLOCK);
- if (lircfd == -1) {
- printf("failed to open lirc device %s: %m\n", argv[1]);
- return 1;
- }
-
- /* Let's try detach it before it was ever attached */
- ret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2);
- if (ret != -ENOENT) {
- printf("bpf_prog_detach2 not attached should fail: %m\n");
- return 1;
- }
-
- inputfd = open(argv[2], O_RDONLY | O_NONBLOCK);
- if (inputfd == -1) {
- printf("failed to open input device %s: %m\n", argv[1]);
- return 1;
- }
-
- prog_cnt = 10;
- ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,
- &prog_cnt);
- if (ret) {
- printf("Failed to query bpf programs on lirc device: %m\n");
- return 1;
- }
-
- if (prog_cnt != 0) {
- printf("Expected nothing to be attached\n");
- return 1;
- }
-
- ret = bpf_prog_attach(progfd, lircfd, BPF_LIRC_MODE2, 0);
- if (ret) {
- printf("Failed to attach bpf to lirc device: %m\n");
- return 1;
- }
-
- /* Write raw IR */
- ret = write(lircfd, &testir1, sizeof(testir1));
- if (ret != sizeof(testir1)) {
- printf("Failed to send test IR message: %m\n");
- return 1;
- }
-
- struct pollfd pfd = { .fd = inputfd, .events = POLLIN };
- struct input_event event;
-
- for (;;) {
- poll(&pfd, 1, 100);
-
- /* Read decoded IR */
- ret = read(inputfd, &event, sizeof(event));
- if (ret != sizeof(event)) {
- printf("Failed to read decoded IR: %m\n");
- return 1;
- }
-
- if (event.type == EV_MSC && event.code == MSC_SCAN &&
- event.value == 0x1ead) {
- break;
- }
- }
-
- /* Write raw IR */
- ret = write(lircfd, &testir2, sizeof(testir2));
- if (ret != sizeof(testir2)) {
- printf("Failed to send test IR message: %m\n");
- return 1;
- }
-
- for (;;) {
- poll(&pfd, 1, 100);
-
- /* Read decoded IR */
- ret = read(inputfd, &event, sizeof(event));
- if (ret != sizeof(event)) {
- printf("Failed to read decoded IR: %m\n");
- return 1;
- }
-
- if (event.type == EV_REL && event.code == REL_Y &&
- event.value == 1 ) {
- break;
- }
- }
-
- prog_cnt = 10;
- ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,
- &prog_cnt);
- if (ret) {
- printf("Failed to query bpf programs on lirc device: %m\n");
- return 1;
- }
-
- if (prog_cnt != 1) {
- printf("Expected one program to be attached\n");
- return 1;
- }
-
- /* Let's try detaching it now it is actually attached */
- ret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2);
- if (ret) {
- printf("bpf_prog_detach2: returned %m\n");
- return 1;
- }
-
- return 0;
-}
|
| 3/2 | 2026/08/20 22:07 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10125 output: 80 thoughts: 380 Thoughts: **BPF Selftest Conversion Review** Okay, so I've got this patch that refactors the `lirc_mode2` BPF selftests. Basically, it's taking an old, clunkier style test and modernizing it, moving it over to the `test_progs` framework. I need to quickly determine if this *actually* changes anything core to the kernel, or if it's just a refactoring of the test itself. First instinct is that it *shouldn't* touch kernel functionality. It's all within the `tools/testing/selftests/bpf/` directory, which is exactly where I expect to see test code. Okay, let's confirm that with a more thorough check of the diff. Alright, the files modified are: `.gitignore`, `Makefile`, and the main test files. Specifically: `prog_tests/lirc_mode2.c`, `progs/lirc_mode2.c`. The old stuff – `test_lirc_mode2_kern.c`, `test_lirc_mode2.sh`, and `test_lirc_mode2_user.c` – are all being *deleted*. So, we're replacing the old test with the new one. No kernel source code is being modified outside of `tools/`. This confirms my initial assessment. Given that this is a purely selftest-related change, I'm confident there are zero functional changes to the kernel itself. This means we're safe to mark `WorthFuzzing=false`. Time to call `set-results`. |
| 4/2 | 2026/08/20 22:07 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch only modifies BPF selftests (tools/testing/selftests/bpf/) by converting an old-style test into the test_progs framework. It does not make any functional changes to the kernel code itself.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies BPF selftests (tools/testing/selftests/bpf/) by converting an old-style test into the test_progs framework. It does not make any functional changes to the kernel code itself.",
"WorthFuzzing": false
} |
| 5/1 | 2026/08/20 22:07 | action | If |
0mArgs:{
"WorthFuzzing": false
}Results: null |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|