Path: blob/master/tools/testing/selftests/hid/progs/hid.c
26308 views
// SPDX-License-Identifier: GPL-2.01/* Copyright (c) 2022 Red hat */2#include "hid_bpf_helpers.h"34char _license[] SEC("license") = "GPL";56struct attach_prog_args {7int prog_fd;8unsigned int hid;9int retval;10int insert_head;11};1213__u64 callback_check = 52;14__u64 callback2_check = 52;1516SEC("?struct_ops/hid_device_event")17int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)18{19__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);2021if (!rw_data)22return 0; /* EPERM check */2324callback_check = rw_data[1];2526rw_data[2] = rw_data[1] + 5;2728return hid_ctx->size;29}3031SEC(".struct_ops.link")32struct hid_bpf_ops first_event = {33.hid_device_event = (void *)hid_first_event,34.hid_id = 2,35};3637int __hid_subprog_first_event(struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)38{39__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);4041if (!rw_data)42return 0; /* EPERM check */4344rw_data[2] = rw_data[1] + 5;4546return hid_ctx->size;47}4849SEC("?struct_ops/hid_device_event")50int BPF_PROG(hid_subprog_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)51{52return __hid_subprog_first_event(hid_ctx, type);53}5455SEC(".struct_ops.link")56struct hid_bpf_ops subprog_first_event = {57.hid_device_event = (void *)hid_subprog_first_event,58.hid_id = 2,59};6061SEC("?struct_ops/hid_device_event")62int BPF_PROG(hid_second_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)63{64__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);6566if (!rw_data)67return 0; /* EPERM check */6869rw_data[3] = rw_data[2] + 5;7071return hid_ctx->size;72}7374SEC(".struct_ops.link")75struct hid_bpf_ops second_event = {76.hid_device_event = (void *)hid_second_event,77};7879SEC("?struct_ops/hid_device_event")80int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)81{82__u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);8384if (!rw_data)85return 0; /* EPERM check */8687rw_data[0] = 2;8889return 9;90}9192SEC(".struct_ops.link")93struct hid_bpf_ops change_report_id = {94.hid_device_event = (void *)hid_change_report_id,95};9697struct hid_hw_request_syscall_args {98/* data needs to come at offset 0 so we can use it in calls */99__u8 data[10];100unsigned int hid;101int retval;102size_t size;103enum hid_report_type type;104__u8 request_type;105};106107SEC("syscall")108int hid_user_raw_request(struct hid_hw_request_syscall_args *args)109{110struct hid_bpf_ctx *ctx;111const size_t size = args->size;112int i, ret = 0;113114if (size > sizeof(args->data))115return -7; /* -E2BIG */116117ctx = hid_bpf_allocate_context(args->hid);118if (!ctx)119return -1; /* EPERM check */120121ret = hid_bpf_hw_request(ctx,122args->data,123size,124args->type,125args->request_type);126args->retval = ret;127128hid_bpf_release_context(ctx);129130return 0;131}132133SEC("syscall")134int hid_user_output_report(struct hid_hw_request_syscall_args *args)135{136struct hid_bpf_ctx *ctx;137const size_t size = args->size;138int i, ret = 0;139140if (size > sizeof(args->data))141return -7; /* -E2BIG */142143ctx = hid_bpf_allocate_context(args->hid);144if (!ctx)145return -1; /* EPERM check */146147ret = hid_bpf_hw_output_report(ctx,148args->data,149size);150args->retval = ret;151152hid_bpf_release_context(ctx);153154return 0;155}156157SEC("syscall")158int hid_user_input_report(struct hid_hw_request_syscall_args *args)159{160struct hid_bpf_ctx *ctx;161const size_t size = args->size;162int i, ret = 0;163164if (size > sizeof(args->data))165return -7; /* -E2BIG */166167ctx = hid_bpf_allocate_context(args->hid);168if (!ctx)169return -1; /* EPERM check */170171ret = hid_bpf_input_report(ctx, HID_INPUT_REPORT, args->data, size);172args->retval = ret;173174hid_bpf_release_context(ctx);175176return 0;177}178179static const __u8 rdesc[] = {1800x05, 0x01, /* USAGE_PAGE (Generic Desktop) */1810x09, 0x32, /* USAGE (Z) */1820x95, 0x01, /* REPORT_COUNT (1) */1830x81, 0x06, /* INPUT (Data,Var,Rel) */1841850x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */1860x19, 0x01, /* USAGE_MINIMUM (1) */1870x29, 0x03, /* USAGE_MAXIMUM (3) */1880x15, 0x00, /* LOGICAL_MINIMUM (0) */1890x25, 0x01, /* LOGICAL_MAXIMUM (1) */1900x95, 0x03, /* REPORT_COUNT (3) */1910x75, 0x01, /* REPORT_SIZE (1) */1920x91, 0x02, /* Output (Data,Var,Abs) */1930x95, 0x01, /* REPORT_COUNT (1) */1940x75, 0x05, /* REPORT_SIZE (5) */1950x91, 0x01, /* Output (Cnst,Var,Abs) */1961970x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */1980x19, 0x06, /* USAGE_MINIMUM (6) */1990x29, 0x08, /* USAGE_MAXIMUM (8) */2000x15, 0x00, /* LOGICAL_MINIMUM (0) */2010x25, 0x01, /* LOGICAL_MAXIMUM (1) */2020x95, 0x03, /* REPORT_COUNT (3) */2030x75, 0x01, /* REPORT_SIZE (1) */2040xb1, 0x02, /* Feature (Data,Var,Abs) */2050x95, 0x01, /* REPORT_COUNT (1) */2060x75, 0x05, /* REPORT_SIZE (5) */2070x91, 0x01, /* Output (Cnst,Var,Abs) */2082090xc0, /* END_COLLECTION */2100xc0, /* END_COLLECTION */211};212213/*214* the following program is marked as sleepable (struct_ops.s).215* This is not strictly mandatory but is a nice test for216* sleepable struct_ops217*/218SEC("?struct_ops.s/hid_rdesc_fixup")219int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx)220{221__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */);222223if (!data)224return 0; /* EPERM check */225226callback2_check = data[4];227228/* insert rdesc at offset 73 */229__builtin_memcpy(&data[73], rdesc, sizeof(rdesc));230231/* Change Usage Vendor globally */232data[4] = 0x42;233234return sizeof(rdesc) + 73;235}236237SEC(".struct_ops.link")238struct hid_bpf_ops rdesc_fixup = {239.hid_rdesc_fixup = (void *)hid_rdesc_fixup,240};241242SEC("?struct_ops/hid_device_event")243int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)244{245__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);246247if (!data)248return 0; /* EPERM check */249250/* we need to be run first */251if (data[2] || data[3])252return -1;253254data[1] = 1;255256return 0;257}258259SEC(".struct_ops.link")260struct hid_bpf_ops test_insert1 = {261.hid_device_event = (void *)hid_test_insert1,262.flags = BPF_F_BEFORE,263};264265SEC("?struct_ops/hid_device_event")266int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)267{268__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);269270if (!data)271return 0; /* EPERM check */272273/* after insert0 and before insert2 */274if (!data[1] || data[3])275return -1;276277data[2] = 2;278279return 0;280}281282SEC(".struct_ops.link")283struct hid_bpf_ops test_insert2 = {284.hid_device_event = (void *)hid_test_insert2,285};286287SEC("?struct_ops/hid_device_event")288int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)289{290__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);291292if (!data)293return 0; /* EPERM check */294295/* at the end */296if (!data[1] || !data[2])297return -1;298299data[3] = 3;300301return 0;302}303304SEC(".struct_ops.link")305struct hid_bpf_ops test_insert3 = {306.hid_device_event = (void *)hid_test_insert3,307};308309SEC("?struct_ops/hid_hw_request")310int BPF_PROG(hid_test_filter_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,311enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)312{313return -20;314}315316SEC(".struct_ops.link")317struct hid_bpf_ops test_filter_raw_request = {318.hid_hw_request = (void *)hid_test_filter_raw_request,319};320321static struct file *current_file;322323SEC("fentry/hidraw_open")324int BPF_PROG(hidraw_open, struct inode *inode, struct file *file)325{326current_file = file;327return 0;328}329330SEC("?struct_ops.s/hid_hw_request")331int BPF_PROG(hid_test_hidraw_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,332enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)333{334__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);335int ret;336337if (!data)338return 0; /* EPERM check */339340/* check if the incoming request comes from our hidraw operation */341if (source == (__u64)current_file) {342data[0] = reportnum;343344ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype);345if (ret != 2)346return -1;347data[0] = reportnum + 1;348data[1] = reportnum + 2;349data[2] = reportnum + 3;350return 3;351}352353return 0;354}355356SEC(".struct_ops.link")357struct hid_bpf_ops test_hidraw_raw_request = {358.hid_hw_request = (void *)hid_test_hidraw_raw_request,359};360361SEC("?struct_ops.s/hid_hw_request")362int BPF_PROG(hid_test_infinite_loop_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,363enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)364{365__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);366int ret;367368if (!data)369return 0; /* EPERM check */370371/* always forward the request as-is to the device, hid-bpf should prevent372* infinite loops.373*/374data[0] = reportnum;375376ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype);377if (ret == 2)378return 3;379380return 0;381}382383SEC(".struct_ops.link")384struct hid_bpf_ops test_infinite_loop_raw_request = {385.hid_hw_request = (void *)hid_test_infinite_loop_raw_request,386};387388SEC("?struct_ops/hid_hw_output_report")389int BPF_PROG(hid_test_filter_output_report, struct hid_bpf_ctx *hctx, unsigned char reportnum,390enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)391{392return -25;393}394395SEC(".struct_ops.link")396struct hid_bpf_ops test_filter_output_report = {397.hid_hw_output_report = (void *)hid_test_filter_output_report,398};399400SEC("?struct_ops.s/hid_hw_output_report")401int BPF_PROG(hid_test_hidraw_output_report, struct hid_bpf_ctx *hctx, __u64 source)402{403__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);404int ret;405406if (!data)407return 0; /* EPERM check */408409/* check if the incoming request comes from our hidraw operation */410if (source == (__u64)current_file)411return hid_bpf_hw_output_report(hctx, data, 2);412413return 0;414}415416SEC(".struct_ops.link")417struct hid_bpf_ops test_hidraw_output_report = {418.hid_hw_output_report = (void *)hid_test_hidraw_output_report,419};420421SEC("?struct_ops.s/hid_hw_output_report")422int BPF_PROG(hid_test_infinite_loop_output_report, struct hid_bpf_ctx *hctx, __u64 source)423{424__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);425int ret;426427if (!data)428return 0; /* EPERM check */429430/* always forward the request as-is to the device, hid-bpf should prevent431* infinite loops.432*/433434ret = hid_bpf_hw_output_report(hctx, data, 2);435if (ret == 2)436return 2;437438return 0;439}440441SEC(".struct_ops.link")442struct hid_bpf_ops test_infinite_loop_output_report = {443.hid_hw_output_report = (void *)hid_test_infinite_loop_output_report,444};445446struct elem {447struct bpf_wq work;448};449450struct {451__uint(type, BPF_MAP_TYPE_HASH);452__uint(max_entries, 1);453__type(key, int);454__type(value, struct elem);455} hmap SEC(".maps");456457static int wq_cb_sleepable(void *map, int *key, void *work)458{459__u8 buf[9] = {2, 3, 4, 5, 6, 7, 8, 9, 10};460struct hid_bpf_ctx *hid_ctx;461462hid_ctx = hid_bpf_allocate_context(*key);463if (!hid_ctx)464return 0; /* EPERM check */465466hid_bpf_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf));467468hid_bpf_release_context(hid_ctx);469470return 0;471}472473static int test_inject_input_report_callback(int *key)474{475struct elem init = {}, *val;476struct bpf_wq *wq;477478if (bpf_map_update_elem(&hmap, key, &init, 0))479return -1;480481val = bpf_map_lookup_elem(&hmap, key);482if (!val)483return -2;484485wq = &val->work;486if (bpf_wq_init(wq, &hmap, 0) != 0)487return -3;488489if (bpf_wq_set_callback(wq, wq_cb_sleepable, 0))490return -4;491492if (bpf_wq_start(wq, 0))493return -5;494495return 0;496}497498SEC("?struct_ops/hid_device_event")499int BPF_PROG(hid_test_multiply_events_wq, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)500{501__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */);502int hid = hid_ctx->hid->id;503int ret;504505if (!data)506return 0; /* EPERM check */507508if (data[0] != 1)509return 0;510511ret = test_inject_input_report_callback(&hid);512if (ret)513return ret;514515data[1] += 5;516517return 0;518}519520SEC(".struct_ops.link")521struct hid_bpf_ops test_multiply_events_wq = {522.hid_device_event = (void *)hid_test_multiply_events_wq,523};524525SEC("?struct_ops/hid_device_event")526int BPF_PROG(hid_test_multiply_events, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)527{528__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */);529__u8 buf[9];530int ret;531532if (!data)533return 0; /* EPERM check */534535if (data[0] != 1)536return 0;537538/*539* we have to use an intermediate buffer as hid_bpf_input_report540* will memset data to \0541*/542__builtin_memcpy(buf, data, sizeof(buf));543544buf[0] = 2;545buf[1] += 5;546ret = hid_bpf_try_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf));547if (ret < 0)548return ret;549550/*551* In real world we should reset the original buffer as data might be garbage now,552* but it actually now has the content of 'buf'553*/554data[1] += 5;555556return 9;557}558559SEC(".struct_ops.link")560struct hid_bpf_ops test_multiply_events = {561.hid_device_event = (void *)hid_test_multiply_events,562};563564SEC("?struct_ops/hid_device_event")565int BPF_PROG(hid_test_infinite_loop_input_report, struct hid_bpf_ctx *hctx,566enum hid_report_type report_type, __u64 source)567{568__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 6 /* size */);569__u8 buf[6];570571if (!data)572return 0; /* EPERM check */573574/*575* we have to use an intermediate buffer as hid_bpf_input_report576* will memset data to \0577*/578__builtin_memcpy(buf, data, sizeof(buf));579580/* always forward the request as-is to the device, hid-bpf should prevent581* infinite loops.582* the return value is ignored so the event is passing to userspace.583*/584585hid_bpf_try_input_report(hctx, report_type, buf, sizeof(buf));586587/* each time we process the event, we increment by one data[1]:588* after each successful call to hid_bpf_try_input_report, buf589* has been memcopied into data by the kernel.590*/591data[1] += 1;592593return 0;594}595596SEC(".struct_ops.link")597struct hid_bpf_ops test_infinite_loop_input_report = {598.hid_device_event = (void *)hid_test_infinite_loop_input_report,599};600601602