/src/hostap/tests/fuzzing/ml-elems/ml-elems.c
Line | Count | Source |
1 | | /* |
2 | | * Multi-Link element parsing - fuzzer |
3 | | * Copyright (c) 2026, Louis Kotze <loukot@gmail.com> |
4 | | * |
5 | | * This software may be distributed under the terms of the BSD license. |
6 | | * See README for more details. |
7 | | */ |
8 | | |
9 | | #include "utils/includes.h" |
10 | | #include "utils/common.h" |
11 | | #include "common/ieee802_11_common.h" |
12 | | #include "common/ieee802_11_defs.h" |
13 | | #include "common/defs.h" |
14 | | #include "../fuzzer-common.h" |
15 | | |
16 | | |
17 | | /* |
18 | | * Drive the per-STA profile parsers the way the callers in bss.c and events.c |
19 | | * do: the buffer starts at the Multi-Link Control field, not at the element |
20 | | * header. Both parsers may defragment subelements in place, so each call gets |
21 | | * its own copy. |
22 | | */ |
23 | | static void fuzz_link_profile(const u8 *mle, size_t mle_len) |
24 | 16.2k | { |
25 | 16.2k | struct ieee802_11_elems elems; |
26 | 16.2k | struct wpabuf *mlbuf; |
27 | 16.2k | u8 link_id; |
28 | | |
29 | 16.2k | if (!mle || !mle_len) |
30 | 14.6k | return; |
31 | | |
32 | 25.1k | for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { |
33 | 23.5k | mlbuf = ieee802_11_defrag(mle, mle_len, true); |
34 | 23.5k | if (!mlbuf) |
35 | 0 | return; |
36 | 23.5k | os_memset(&elems, 0, sizeof(elems)); |
37 | 23.5k | ieee802_11_parse_link_assoc_req(&elems, mlbuf, link_id, 1); |
38 | 23.5k | wpabuf_free(mlbuf); |
39 | | |
40 | 23.5k | mlbuf = ieee802_11_defrag(mle, mle_len, true); |
41 | 23.5k | if (!mlbuf) |
42 | 0 | return; |
43 | 23.5k | os_memset(&elems, 0, sizeof(elems)); |
44 | 23.5k | ieee802_11_parse_link_assoc_resp(&elems, mlbuf, link_id, 1); |
45 | 23.5k | wpabuf_free(mlbuf); |
46 | 23.5k | } |
47 | | |
48 | | /* parent_subelem must point inside mlbuf and, as every in-tree caller |
49 | | * guarantees by looping on "len > 2", must have a readable two octet |
50 | | * subelement header. Passing anything shorter breaks the callee's |
51 | | * contract and would only report a defect in this harness. */ |
52 | 1.57k | if (mle_len >= 2) { |
53 | 1.57k | mlbuf = ieee802_11_defrag(mle, mle_len, true); |
54 | 1.57k | if (mlbuf) { |
55 | 1.57k | size_t defrag_len = 0; |
56 | | |
57 | 1.57k | ieee802_11_defrag_mle_subelem(mlbuf, wpabuf_head(mlbuf), |
58 | 1.57k | &defrag_len); |
59 | 1.57k | wpabuf_free(mlbuf); |
60 | 1.57k | } |
61 | 1.57k | } |
62 | 1.57k | } |
63 | | |
64 | | |
65 | | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) |
66 | 3.25k | { |
67 | 3.25k | struct ieee802_11_elems elems; |
68 | 3.25k | struct wpabuf *buf; |
69 | 3.25k | u8 type; |
70 | | |
71 | 3.25k | wpa_fuzzer_set_debug_level(); |
72 | | |
73 | 3.25k | if (size > 10000) |
74 | 8 | return 0; |
75 | | |
76 | | /* Full element parsing; dispatches to the Multi-Link element handling |
77 | | * and to element fragment reassembly. */ |
78 | 3.25k | ieee802_11_parse_elems(data, size, &elems, 1); |
79 | | |
80 | | /* Each Multi-Link element variant the parser recognised, handed on in |
81 | | * the same shape the real callers use. */ |
82 | 3.25k | fuzz_link_profile(elems.basic_mle, elems.basic_mle_len); |
83 | 3.25k | fuzz_link_profile(elems.probe_req_mle, elems.probe_req_mle_len); |
84 | 3.25k | fuzz_link_profile(elems.reconf_mle, elems.reconf_mle_len); |
85 | 3.25k | fuzz_link_profile(elems.tdls_mle, elems.tdls_mle_len); |
86 | 3.25k | fuzz_link_profile(elems.prior_access_mle, elems.prior_access_mle_len); |
87 | | |
88 | | /* Basic Multi-Link element accessors, both on the element payload the |
89 | | * parser found and on the raw input, so the short-buffer rejection |
90 | | * paths are covered too. */ |
91 | 3.25k | if (elems.basic_mle) { |
92 | 1.45k | get_basic_mle_mld_addr(elems.basic_mle, elems.basic_mle_len); |
93 | 1.45k | get_basic_mle_eml_capa(elems.basic_mle, elems.basic_mle_len); |
94 | 1.45k | get_basic_mle_link_id(elems.basic_mle, elems.basic_mle_len); |
95 | 1.45k | } |
96 | 3.25k | get_basic_mle_mld_addr(data, size); |
97 | 3.25k | get_basic_mle_eml_capa(data, size); |
98 | 3.25k | get_basic_mle_link_id(data, size); |
99 | | |
100 | 29.2k | for (type = 0; type < 8; type++) |
101 | 26.0k | get_ml_ie(data, size, type); |
102 | | |
103 | | /* Element defragmentation, both element and extended-element forms. */ |
104 | 3.25k | buf = ieee802_11_defrag(data, size, true); |
105 | 3.25k | if (buf) { |
106 | | /* Also drive subelement defragmentation straight off the input, |
107 | | * not only via a Multi-Link element the parser recognised. */ |
108 | 3.25k | if (wpabuf_len(buf) >= 2) { |
109 | 3.24k | size_t defrag_len = 0; |
110 | | |
111 | 3.24k | ieee802_11_defrag_mle_subelem(buf, wpabuf_head(buf), |
112 | 3.24k | &defrag_len); |
113 | 3.24k | } |
114 | 3.25k | wpabuf_free(buf); |
115 | 3.25k | } |
116 | 3.25k | buf = ieee802_11_defrag(data, size, false); |
117 | 3.25k | wpabuf_free(buf); |
118 | | |
119 | 3.25k | return 0; |
120 | 3.25k | } |