/src/tinyusb/test/fuzz/dcd_fuzz.cc
Line | Count | Source |
1 | | /* |
2 | | * The MIT License (MIT) |
3 | | * |
4 | | * Copyright (c) 2022 Nathaniel Brough |
5 | | * |
6 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | | * of this software and associated documentation files (the "Software"), to deal |
8 | | * in the Software without restriction, including without limitation the rights |
9 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
10 | | * copies of the Software, and to permit persons to whom the Software is |
11 | | * furnished to do so, subject to the following conditions: |
12 | | * |
13 | | * The above copyright notice and this permission notice shall be included in |
14 | | * all copies or substantial portions of the Software. |
15 | | * |
16 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
19 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
22 | | * THE SOFTWARE. |
23 | | * |
24 | | */ |
25 | | #include "device/dcd.h" |
26 | | #include "fuzz/fuzz_private.h" |
27 | | #include <assert.h> |
28 | | #include <cstdint> |
29 | | #include <limits> |
30 | | |
31 | 1.93M | #define UNUSED(x) (void)(x) |
32 | | |
33 | | //--------------------------------------------------------------------+ |
34 | | // State tracker |
35 | | //--------------------------------------------------------------------+ |
36 | | struct State { |
37 | | bool interrupts_enabled; |
38 | | bool sof_enabled; |
39 | | uint8_t address; |
40 | | }; |
41 | | |
42 | | tu_static State state = {false, 0, 0}; |
43 | | |
44 | | //--------------------------------------------------------------------+ |
45 | | // Controller API |
46 | | // All no-ops as we are fuzzing. |
47 | | //--------------------------------------------------------------------+ |
48 | | extern "C" { |
49 | 3 | bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { |
50 | 3 | UNUSED(rhport); |
51 | 3 | UNUSED(rh_init); |
52 | 3 | return true; |
53 | 3 | } |
54 | | |
55 | 288k | void dcd_int_handler(uint8_t rhport) { |
56 | 288k | assert(_fuzz_data_provider.has_value()); |
57 | | |
58 | 288k | if (!state.interrupts_enabled) { |
59 | 0 | return; |
60 | 0 | } |
61 | | |
62 | | // Choose if we want to generate a signal based on the fuzzed data. |
63 | 288k | if (_fuzz_data_provider->ConsumeBool()) { |
64 | | // Only generate bus signal events that don't carry additional union data. |
65 | | // DCD_EVENT_XFER_COMPLETE, DCD_EVENT_SOF, and DCD_EVENT_BUS_RESET need |
66 | | // properly initialized union fields; USBD_EVENT_FUNC_CALL is internal only. |
67 | | // Valid bus-signal-only events: UNPLUGGED(2), SUSPEND(4), RESUME(5). |
68 | 85.9k | static const dcd_eventid_t bus_signal_events[] = { |
69 | 85.9k | DCD_EVENT_UNPLUGGED, DCD_EVENT_SUSPEND, DCD_EVENT_RESUME}; |
70 | 85.9k | uint8_t idx = _fuzz_data_provider->ConsumeIntegralInRange<uint8_t>(0, 2); |
71 | 85.9k | dcd_event_bus_signal(rhport, bus_signal_events[idx], |
72 | 85.9k | _fuzz_data_provider->ConsumeBool()); |
73 | 85.9k | } |
74 | | |
75 | | // Optionally generate a BUS_RESET event with a valid speed value. |
76 | 288k | if (_fuzz_data_provider->ConsumeBool()) { |
77 | 95.7k | tusb_speed_t speed = (tusb_speed_t)_fuzz_data_provider->ConsumeIntegralInRange<uint8_t>( |
78 | 95.7k | TUSB_SPEED_FULL, TUSB_SPEED_HIGH); |
79 | 95.7k | dcd_event_bus_reset(rhport, speed, _fuzz_data_provider->ConsumeBool()); |
80 | 95.7k | } |
81 | | |
82 | 288k | if (_fuzz_data_provider->ConsumeBool()) { |
83 | 100k | constexpr size_t kSetupFrameLength = 8; |
84 | 100k | std::vector<uint8_t> setup = |
85 | 100k | _fuzz_data_provider->ConsumeBytes<uint8_t>(kSetupFrameLength); |
86 | | // Fuzz consumer may return less than requested. If this is the case |
87 | | // we want to make sure that at least that length is allocated and available |
88 | | // to the signal handler. |
89 | 100k | if (setup.size() != kSetupFrameLength) { |
90 | 757 | setup.resize(kSetupFrameLength); |
91 | 757 | } |
92 | 100k | dcd_event_setup_received(rhport, setup.data(), |
93 | | // Identify trigger as either an interrupt or a |
94 | | // syncrhonous call depending on fuzz data. |
95 | 100k | _fuzz_data_provider->ConsumeBool()); |
96 | 100k | } |
97 | 288k | } |
98 | | |
99 | 643k | void dcd_int_enable(uint8_t rhport) { |
100 | 643k | state.interrupts_enabled = true; |
101 | 643k | UNUSED(rhport); |
102 | 643k | return; |
103 | 643k | } |
104 | | |
105 | 643k | void dcd_int_disable(uint8_t rhport) { |
106 | 643k | state.interrupts_enabled = false; |
107 | 643k | UNUSED(rhport); |
108 | 643k | return; |
109 | 643k | } |
110 | | |
111 | 193 | void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { |
112 | 193 | UNUSED(rhport); |
113 | 193 | state.address = dev_addr; |
114 | | // Respond with status. |
115 | 193 | dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0, false); |
116 | 193 | return; |
117 | 193 | } |
118 | | |
119 | 0 | void dcd_remote_wakeup(uint8_t rhport) { |
120 | 0 | UNUSED(rhport); |
121 | 0 | return; |
122 | 0 | } |
123 | | |
124 | | void dcd_connect(uint8_t rhport) { |
125 | | UNUSED(rhport); |
126 | | return; |
127 | | } |
128 | | |
129 | | void dcd_disconnect(uint8_t rhport) { |
130 | | UNUSED(rhport); |
131 | | return; |
132 | | } |
133 | | |
134 | 13 | void dcd_sof_enable(uint8_t rhport, bool en) { |
135 | 13 | state.sof_enabled = en; |
136 | 13 | UNUSED(rhport); |
137 | 13 | return; |
138 | 13 | } |
139 | | |
140 | | //--------------------------------------------------------------------+ |
141 | | // Endpoint API |
142 | | //--------------------------------------------------------------------+ |
143 | | |
144 | | // Configure endpoint's registers according to descriptor |
145 | 14.3k | bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) { |
146 | 14.3k | UNUSED(rhport); |
147 | 14.3k | UNUSED(desc_ep); |
148 | 14.3k | return _fuzz_data_provider->ConsumeBool(); |
149 | 14.3k | } |
150 | | |
151 | | // Close all non-control endpoints, cancel all pending transfers if any. |
152 | | // Invoked when switching from a non-zero Configuration by SET_CONFIGURE |
153 | | // therefore required for multiple configuration support. |
154 | 13 | void dcd_edpt_close_all(uint8_t rhport) { |
155 | 13 | UNUSED(rhport); |
156 | 13 | return; |
157 | 13 | } |
158 | | |
159 | | // Close an endpoint. |
160 | | // Since it is weak, caller must TU_ASSERT this function's existence before |
161 | | // calling it. |
162 | 0 | void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { |
163 | 0 | UNUSED(rhport); |
164 | 0 | UNUSED(ep_addr); |
165 | 0 | return; |
166 | 0 | } |
167 | | |
168 | | // Submit a transfer, When complete dcd_event_xfer_complete() is invoked to |
169 | | // notify the stack |
170 | | bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, |
171 | 50.7k | uint16_t total_bytes, bool is_isr) { |
172 | 50.7k | UNUSED(rhport); |
173 | 50.7k | UNUSED(buffer); |
174 | 50.7k | UNUSED(total_bytes); |
175 | 50.7k | UNUSED(is_isr); |
176 | | |
177 | 50.7k | uint8_t const dir = tu_edpt_dir(ep_addr); |
178 | | |
179 | 50.7k | if (dir == TUSB_DIR_IN) { |
180 | 13.6k | std::vector<uint8_t> temp = |
181 | 13.6k | _fuzz_data_provider->ConsumeBytes<uint8_t>(total_bytes); |
182 | 13.6k | std::copy(temp.begin(), temp.end(), buffer); |
183 | 13.6k | } |
184 | | // Ignore output data as it's not useful for fuzzing without a more |
185 | | // complex fuzzed backend. But we need to make sure it's not |
186 | | // optimised out. |
187 | 50.7k | volatile uint8_t *dont_optimise0 = buffer; |
188 | 50.7k | volatile uint16_t dont_optimise1 = total_bytes; |
189 | 50.7k | UNUSED(dont_optimise0); |
190 | 50.7k | UNUSED(dont_optimise1); |
191 | | |
192 | | |
193 | 50.7k | return _fuzz_data_provider->ConsumeBool(); |
194 | 50.7k | } |
195 | | |
196 | | /* TODO: implement a fuzzed version of this. |
197 | | bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, |
198 | | uint16_t total_bytes) {} |
199 | | */ |
200 | | |
201 | | // Stall endpoint, any queuing transfer should be removed from endpoint |
202 | 158k | void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { |
203 | | |
204 | 158k | UNUSED(rhport); |
205 | 158k | UNUSED(ep_addr); |
206 | 158k | return; |
207 | 158k | } |
208 | | |
209 | | // clear stall, data toggle is also reset to DATA0 |
210 | | // This API never calls with control endpoints, since it is auto cleared when |
211 | | // receiving setup packet |
212 | 275 | void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { |
213 | | |
214 | 275 | UNUSED(rhport); |
215 | 275 | UNUSED(ep_addr); |
216 | 275 | return; |
217 | 275 | } |
218 | | } |