hid.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. // SPDX-License-Identifier: GPL-2.0
  2. /* Copyright (c) 2022 Red hat */
  3. #include "hid_bpf_helpers.h"
  4. char _license[] SEC("license") = "GPL";
  5. struct attach_prog_args {
  6. int prog_fd;
  7. unsigned int hid;
  8. int retval;
  9. int insert_head;
  10. };
  11. __u64 callback_check = 52;
  12. __u64 callback2_check = 52;
  13. SEC("?struct_ops/hid_device_event")
  14. int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
  15. {
  16. __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);
  17. if (!rw_data)
  18. return 0; /* EPERM check */
  19. callback_check = rw_data[1];
  20. rw_data[2] = rw_data[1] + 5;
  21. return hid_ctx->size;
  22. }
  23. SEC(".struct_ops.link")
  24. struct hid_bpf_ops first_event = {
  25. .hid_device_event = (void *)hid_first_event,
  26. .hid_id = 2,
  27. };
  28. int __hid_subprog_first_event(struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
  29. {
  30. __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);
  31. if (!rw_data)
  32. return 0; /* EPERM check */
  33. rw_data[2] = rw_data[1] + 5;
  34. return hid_ctx->size;
  35. }
  36. SEC("?struct_ops/hid_device_event")
  37. int BPF_PROG(hid_subprog_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
  38. {
  39. return __hid_subprog_first_event(hid_ctx, type);
  40. }
  41. SEC(".struct_ops.link")
  42. struct hid_bpf_ops subprog_first_event = {
  43. .hid_device_event = (void *)hid_subprog_first_event,
  44. .hid_id = 2,
  45. };
  46. SEC("?struct_ops/hid_device_event")
  47. int BPF_PROG(hid_second_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
  48. {
  49. __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
  50. if (!rw_data)
  51. return 0; /* EPERM check */
  52. rw_data[3] = rw_data[2] + 5;
  53. return hid_ctx->size;
  54. }
  55. SEC(".struct_ops.link")
  56. struct hid_bpf_ops second_event = {
  57. .hid_device_event = (void *)hid_second_event,
  58. };
  59. SEC("?struct_ops/hid_device_event")
  60. int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
  61. {
  62. __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);
  63. if (!rw_data)
  64. return 0; /* EPERM check */
  65. rw_data[0] = 2;
  66. return 9;
  67. }
  68. SEC(".struct_ops.link")
  69. struct hid_bpf_ops change_report_id = {
  70. .hid_device_event = (void *)hid_change_report_id,
  71. };
  72. struct hid_hw_request_syscall_args {
  73. /* data needs to come at offset 0 so we can use it in calls */
  74. __u8 data[10];
  75. unsigned int hid;
  76. int retval;
  77. size_t size;
  78. enum hid_report_type type;
  79. __u8 request_type;
  80. };
  81. SEC("syscall")
  82. int hid_user_raw_request(struct hid_hw_request_syscall_args *args)
  83. {
  84. struct hid_bpf_ctx *ctx;
  85. const size_t size = args->size;
  86. int i, ret = 0;
  87. if (size > sizeof(args->data))
  88. return -7; /* -E2BIG */
  89. ctx = hid_bpf_allocate_context(args->hid);
  90. if (!ctx)
  91. return -1; /* EPERM check */
  92. ret = hid_bpf_hw_request(ctx,
  93. args->data,
  94. size,
  95. args->type,
  96. args->request_type);
  97. args->retval = ret;
  98. hid_bpf_release_context(ctx);
  99. return 0;
  100. }
  101. SEC("syscall")
  102. int hid_user_output_report(struct hid_hw_request_syscall_args *args)
  103. {
  104. struct hid_bpf_ctx *ctx;
  105. const size_t size = args->size;
  106. int i, ret = 0;
  107. if (size > sizeof(args->data))
  108. return -7; /* -E2BIG */
  109. ctx = hid_bpf_allocate_context(args->hid);
  110. if (!ctx)
  111. return -1; /* EPERM check */
  112. ret = hid_bpf_hw_output_report(ctx,
  113. args->data,
  114. size);
  115. args->retval = ret;
  116. hid_bpf_release_context(ctx);
  117. return 0;
  118. }
  119. SEC("syscall")
  120. int hid_user_input_report(struct hid_hw_request_syscall_args *args)
  121. {
  122. struct hid_bpf_ctx *ctx;
  123. const size_t size = args->size;
  124. int i, ret = 0;
  125. if (size > sizeof(args->data))
  126. return -7; /* -E2BIG */
  127. ctx = hid_bpf_allocate_context(args->hid);
  128. if (!ctx)
  129. return -1; /* EPERM check */
  130. ret = hid_bpf_input_report(ctx, HID_INPUT_REPORT, args->data, size);
  131. args->retval = ret;
  132. hid_bpf_release_context(ctx);
  133. return 0;
  134. }
  135. static const __u8 rdesc[] = {
  136. 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
  137. 0x09, 0x32, /* USAGE (Z) */
  138. 0x95, 0x01, /* REPORT_COUNT (1) */
  139. 0x81, 0x06, /* INPUT (Data,Var,Rel) */
  140. 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
  141. 0x19, 0x01, /* USAGE_MINIMUM (1) */
  142. 0x29, 0x03, /* USAGE_MAXIMUM (3) */
  143. 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
  144. 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
  145. 0x95, 0x03, /* REPORT_COUNT (3) */
  146. 0x75, 0x01, /* REPORT_SIZE (1) */
  147. 0x91, 0x02, /* Output (Data,Var,Abs) */
  148. 0x95, 0x01, /* REPORT_COUNT (1) */
  149. 0x75, 0x05, /* REPORT_SIZE (5) */
  150. 0x91, 0x01, /* Output (Cnst,Var,Abs) */
  151. 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
  152. 0x19, 0x06, /* USAGE_MINIMUM (6) */
  153. 0x29, 0x08, /* USAGE_MAXIMUM (8) */
  154. 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
  155. 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
  156. 0x95, 0x03, /* REPORT_COUNT (3) */
  157. 0x75, 0x01, /* REPORT_SIZE (1) */
  158. 0xb1, 0x02, /* Feature (Data,Var,Abs) */
  159. 0x95, 0x01, /* REPORT_COUNT (1) */
  160. 0x75, 0x05, /* REPORT_SIZE (5) */
  161. 0x91, 0x01, /* Output (Cnst,Var,Abs) */
  162. 0xc0, /* END_COLLECTION */
  163. 0xc0, /* END_COLLECTION */
  164. };
  165. /*
  166. * the following program is marked as sleepable (struct_ops.s).
  167. * This is not strictly mandatory but is a nice test for
  168. * sleepable struct_ops
  169. */
  170. SEC("?struct_ops.s/hid_rdesc_fixup")
  171. int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx)
  172. {
  173. __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */);
  174. if (!data)
  175. return 0; /* EPERM check */
  176. callback2_check = data[4];
  177. /* insert rdesc at offset 73 */
  178. __builtin_memcpy(&data[73], rdesc, sizeof(rdesc));
  179. /* Change Usage Vendor globally */
  180. data[4] = 0x42;
  181. return sizeof(rdesc) + 73;
  182. }
  183. SEC(".struct_ops.link")
  184. struct hid_bpf_ops rdesc_fixup = {
  185. .hid_rdesc_fixup = (void *)hid_rdesc_fixup,
  186. };
  187. SEC("?struct_ops/hid_device_event")
  188. int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
  189. {
  190. __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
  191. if (!data)
  192. return 0; /* EPERM check */
  193. /* we need to be run first */
  194. if (data[2] || data[3])
  195. return -1;
  196. data[1] = 1;
  197. return 0;
  198. }
  199. SEC(".struct_ops.link")
  200. struct hid_bpf_ops test_insert1 = {
  201. .hid_device_event = (void *)hid_test_insert1,
  202. .flags = BPF_F_BEFORE,
  203. };
  204. SEC("?struct_ops/hid_device_event")
  205. int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
  206. {
  207. __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
  208. if (!data)
  209. return 0; /* EPERM check */
  210. /* after insert0 and before insert2 */
  211. if (!data[1] || data[3])
  212. return -1;
  213. data[2] = 2;
  214. return 0;
  215. }
  216. SEC(".struct_ops.link")
  217. struct hid_bpf_ops test_insert2 = {
  218. .hid_device_event = (void *)hid_test_insert2,
  219. };
  220. SEC("?struct_ops/hid_device_event")
  221. int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
  222. {
  223. __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
  224. if (!data)
  225. return 0; /* EPERM check */
  226. /* at the end */
  227. if (!data[1] || !data[2])
  228. return -1;
  229. data[3] = 3;
  230. return 0;
  231. }
  232. SEC(".struct_ops.link")
  233. struct hid_bpf_ops test_insert3 = {
  234. .hid_device_event = (void *)hid_test_insert3,
  235. };
  236. SEC("?struct_ops/hid_hw_request")
  237. int BPF_PROG(hid_test_filter_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,
  238. enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
  239. {
  240. return -20;
  241. }
  242. SEC(".struct_ops.link")
  243. struct hid_bpf_ops test_filter_raw_request = {
  244. .hid_hw_request = (void *)hid_test_filter_raw_request,
  245. };
  246. static struct file *current_file;
  247. SEC("fentry/hidraw_open")
  248. int BPF_PROG(hidraw_open, struct inode *inode, struct file *file)
  249. {
  250. current_file = file;
  251. return 0;
  252. }
  253. SEC("?struct_ops.s/hid_hw_request")
  254. int BPF_PROG(hid_test_hidraw_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,
  255. enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
  256. {
  257. __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
  258. int ret;
  259. if (!data)
  260. return 0; /* EPERM check */
  261. /* check if the incoming request comes from our hidraw operation */
  262. if (source == (__u64)current_file) {
  263. data[0] = reportnum;
  264. ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype);
  265. if (ret != 2)
  266. return -1;
  267. data[0] = reportnum + 1;
  268. data[1] = reportnum + 2;
  269. data[2] = reportnum + 3;
  270. return 3;
  271. }
  272. return 0;
  273. }
  274. SEC(".struct_ops.link")
  275. struct hid_bpf_ops test_hidraw_raw_request = {
  276. .hid_hw_request = (void *)hid_test_hidraw_raw_request,
  277. };
  278. SEC("?struct_ops.s/hid_hw_request")
  279. int BPF_PROG(hid_test_infinite_loop_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,
  280. enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
  281. {
  282. __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
  283. int ret;
  284. if (!data)
  285. return 0; /* EPERM check */
  286. /* always forward the request as-is to the device, hid-bpf should prevent
  287. * infinite loops.
  288. */
  289. data[0] = reportnum;
  290. ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype);
  291. if (ret == 2)
  292. return 3;
  293. return 0;
  294. }
  295. SEC(".struct_ops.link")
  296. struct hid_bpf_ops test_infinite_loop_raw_request = {
  297. .hid_hw_request = (void *)hid_test_infinite_loop_raw_request,
  298. };
  299. SEC("?struct_ops/hid_hw_output_report")
  300. int BPF_PROG(hid_test_filter_output_report, struct hid_bpf_ctx *hctx, unsigned char reportnum,
  301. enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
  302. {
  303. return -25;
  304. }
  305. SEC(".struct_ops.link")
  306. struct hid_bpf_ops test_filter_output_report = {
  307. .hid_hw_output_report = (void *)hid_test_filter_output_report,
  308. };
  309. SEC("?struct_ops.s/hid_hw_output_report")
  310. int BPF_PROG(hid_test_hidraw_output_report, struct hid_bpf_ctx *hctx, __u64 source)
  311. {
  312. __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
  313. int ret;
  314. if (!data)
  315. return 0; /* EPERM check */
  316. /* check if the incoming request comes from our hidraw operation */
  317. if (source == (__u64)current_file)
  318. return hid_bpf_hw_output_report(hctx, data, 2);
  319. return 0;
  320. }
  321. SEC(".struct_ops.link")
  322. struct hid_bpf_ops test_hidraw_output_report = {
  323. .hid_hw_output_report = (void *)hid_test_hidraw_output_report,
  324. };
  325. SEC("?struct_ops.s/hid_hw_output_report")
  326. int BPF_PROG(hid_test_infinite_loop_output_report, struct hid_bpf_ctx *hctx, __u64 source)
  327. {
  328. __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
  329. int ret;
  330. if (!data)
  331. return 0; /* EPERM check */
  332. /* always forward the request as-is to the device, hid-bpf should prevent
  333. * infinite loops.
  334. */
  335. ret = hid_bpf_hw_output_report(hctx, data, 2);
  336. if (ret == 2)
  337. return 2;
  338. return 0;
  339. }
  340. SEC(".struct_ops.link")
  341. struct hid_bpf_ops test_infinite_loop_output_report = {
  342. .hid_hw_output_report = (void *)hid_test_infinite_loop_output_report,
  343. };
  344. struct elem {
  345. struct bpf_wq work;
  346. };
  347. struct {
  348. __uint(type, BPF_MAP_TYPE_HASH);
  349. __uint(max_entries, 1);
  350. __type(key, int);
  351. __type(value, struct elem);
  352. } hmap SEC(".maps");
  353. static int wq_cb_sleepable(void *map, int *key, void *work)
  354. {
  355. __u8 buf[9] = {2, 3, 4, 5, 6, 7, 8, 9, 10};
  356. struct hid_bpf_ctx *hid_ctx;
  357. hid_ctx = hid_bpf_allocate_context(*key);
  358. if (!hid_ctx)
  359. return 0; /* EPERM check */
  360. hid_bpf_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf));
  361. hid_bpf_release_context(hid_ctx);
  362. return 0;
  363. }
  364. static int test_inject_input_report_callback(int *key)
  365. {
  366. struct elem init = {}, *val;
  367. struct bpf_wq *wq;
  368. if (bpf_map_update_elem(&hmap, key, &init, 0))
  369. return -1;
  370. val = bpf_map_lookup_elem(&hmap, key);
  371. if (!val)
  372. return -2;
  373. wq = &val->work;
  374. if (bpf_wq_init(wq, &hmap, 0) != 0)
  375. return -3;
  376. if (bpf_wq_set_callback(wq, wq_cb_sleepable, 0))
  377. return -4;
  378. if (bpf_wq_start(wq, 0))
  379. return -5;
  380. return 0;
  381. }
  382. SEC("?struct_ops/hid_device_event")
  383. int BPF_PROG(hid_test_multiply_events_wq, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
  384. {
  385. __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */);
  386. int hid = hid_ctx->hid->id;
  387. int ret;
  388. if (!data)
  389. return 0; /* EPERM check */
  390. if (data[0] != 1)
  391. return 0;
  392. ret = test_inject_input_report_callback(&hid);
  393. if (ret)
  394. return ret;
  395. data[1] += 5;
  396. return 0;
  397. }
  398. SEC(".struct_ops.link")
  399. struct hid_bpf_ops test_multiply_events_wq = {
  400. .hid_device_event = (void *)hid_test_multiply_events_wq,
  401. };
  402. SEC("?struct_ops/hid_device_event")
  403. int BPF_PROG(hid_test_multiply_events, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
  404. {
  405. __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */);
  406. __u8 buf[9];
  407. int ret;
  408. if (!data)
  409. return 0; /* EPERM check */
  410. if (data[0] != 1)
  411. return 0;
  412. /*
  413. * we have to use an intermediate buffer as hid_bpf_input_report
  414. * will memset data to \0
  415. */
  416. __builtin_memcpy(buf, data, sizeof(buf));
  417. buf[0] = 2;
  418. buf[1] += 5;
  419. ret = hid_bpf_try_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf));
  420. if (ret < 0)
  421. return ret;
  422. /*
  423. * In real world we should reset the original buffer as data might be garbage now,
  424. * but it actually now has the content of 'buf'
  425. */
  426. data[1] += 5;
  427. return 9;
  428. }
  429. SEC(".struct_ops.link")
  430. struct hid_bpf_ops test_multiply_events = {
  431. .hid_device_event = (void *)hid_test_multiply_events,
  432. };
  433. SEC("?struct_ops/hid_device_event")
  434. int BPF_PROG(hid_test_infinite_loop_input_report, struct hid_bpf_ctx *hctx,
  435. enum hid_report_type report_type, __u64 source)
  436. {
  437. __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 6 /* size */);
  438. __u8 buf[6];
  439. if (!data)
  440. return 0; /* EPERM check */
  441. /*
  442. * we have to use an intermediate buffer as hid_bpf_input_report
  443. * will memset data to \0
  444. */
  445. __builtin_memcpy(buf, data, sizeof(buf));
  446. /* always forward the request as-is to the device, hid-bpf should prevent
  447. * infinite loops.
  448. * the return value is ignored so the event is passing to userspace.
  449. */
  450. hid_bpf_try_input_report(hctx, report_type, buf, sizeof(buf));
  451. /* each time we process the event, we increment by one data[1]:
  452. * after each successful call to hid_bpf_try_input_report, buf
  453. * has been memcopied into data by the kernel.
  454. */
  455. data[1] += 1;
  456. return 0;
  457. }
  458. SEC(".struct_ops.link")
  459. struct hid_bpf_ops test_infinite_loop_input_report = {
  460. .hid_device_event = (void *)hid_test_infinite_loop_input_report,
  461. };