1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> |
4 | |
5 | This software is provided 'as-is', without any express or implied |
6 | warranty. In no event will the authors be held liable for any damages |
7 | arising from the use of this software. |
8 | |
9 | Permission is granted to anyone to use this software for any purpose, |
10 | including commercial applications, and to alter it and redistribute it |
11 | freely, subject to the following restrictions: |
12 | |
13 | 1. The origin of this software must not be misrepresented; you must not |
14 | claim that you wrote the original software. If you use this software |
15 | in a product, an acknowledgment in the product documentation would be |
16 | appreciated but is not required. |
17 | 2. Altered source versions must be plainly marked as such, and must not be |
18 | misrepresented as being the original software. |
19 | 3. This notice may not be removed or altered from any source distribution. |
20 | */ |
21 | |
22 | /* |
23 | |
24 | Data generators for fuzzing test data in a reproducible way. |
25 | |
26 | */ |
27 | #include <SDL3/SDL_test.h> |
28 | |
29 | #include <float.h> /* Needed for FLT_MAX and DBL_EPSILON */ |
30 | #include <limits.h> /* Needed for UCHAR_MAX, etc. */ |
31 | |
32 | /** |
33 | * Counter for fuzzer invocations |
34 | */ |
35 | static int fuzzerInvocationCounter = 0; |
36 | |
37 | /** |
38 | * Context for shared random number generator |
39 | */ |
40 | static Uint64 rndContext; |
41 | |
42 | /* |
43 | * Note: doxygen documentation markup for functions is in the header file. |
44 | */ |
45 | |
46 | void SDLTest_FuzzerInit(Uint64 execKey) |
47 | { |
48 | rndContext = execKey; |
49 | fuzzerInvocationCounter = 0; |
50 | } |
51 | |
52 | int SDLTest_GetFuzzerInvocationCount(void) |
53 | { |
54 | return fuzzerInvocationCounter; |
55 | } |
56 | |
57 | Uint8 SDLTest_RandomUint8(void) |
58 | { |
59 | fuzzerInvocationCounter++; |
60 | |
61 | return (Uint8)(SDL_rand_bits_r(&rndContext) >> 24); |
62 | } |
63 | |
64 | Sint8 SDLTest_RandomSint8(void) |
65 | { |
66 | fuzzerInvocationCounter++; |
67 | |
68 | return (Sint8)(SDL_rand_bits_r(&rndContext) >> 24); |
69 | } |
70 | |
71 | Uint16 SDLTest_RandomUint16(void) |
72 | { |
73 | fuzzerInvocationCounter++; |
74 | |
75 | return (Uint16)(SDL_rand_bits_r(&rndContext) >> 16); |
76 | } |
77 | |
78 | Sint16 SDLTest_RandomSint16(void) |
79 | { |
80 | fuzzerInvocationCounter++; |
81 | |
82 | return (Sint16)(SDL_rand_bits_r(&rndContext) >> 16); |
83 | } |
84 | |
85 | Uint32 SDLTest_RandomUint32(void) |
86 | { |
87 | fuzzerInvocationCounter++; |
88 | |
89 | return SDL_rand_bits_r(&rndContext); |
90 | } |
91 | |
92 | Sint32 SDLTest_RandomSint32(void) |
93 | { |
94 | fuzzerInvocationCounter++; |
95 | |
96 | return (Sint32)SDL_rand_bits_r(&rndContext); |
97 | } |
98 | |
99 | Uint64 SDLTest_RandomUint64(void) |
100 | { |
101 | union |
102 | { |
103 | Uint64 v64; |
104 | Uint32 v32[2]; |
105 | } value; |
106 | |
107 | fuzzerInvocationCounter++; |
108 | |
109 | value.v32[0] = SDLTest_RandomUint32(); |
110 | value.v32[1] = SDLTest_RandomUint32(); |
111 | |
112 | return value.v64; |
113 | } |
114 | |
115 | Sint64 SDLTest_RandomSint64(void) |
116 | { |
117 | union |
118 | { |
119 | Uint64 v64; |
120 | Uint32 v32[2]; |
121 | } value; |
122 | |
123 | fuzzerInvocationCounter++; |
124 | |
125 | value.v32[0] = SDLTest_RandomUint32(); |
126 | value.v32[1] = SDLTest_RandomUint32(); |
127 | |
128 | return (Sint64)value.v64; |
129 | } |
130 | |
131 | Sint32 SDLTest_RandomIntegerInRange(Sint32 min, Sint32 max) |
132 | { |
133 | fuzzerInvocationCounter++; |
134 | |
135 | if (min == max) { |
136 | return min; |
137 | } |
138 | |
139 | if (min > max) { |
140 | Sint32 temp = min; |
141 | min = max; |
142 | max = temp; |
143 | } |
144 | |
145 | Uint64 range = (Sint64)max - (Sint64)min; |
146 | if (range < SDL_MAX_SINT32) { |
147 | return min + SDL_rand_r(&rndContext, (Sint32) range + 1); |
148 | } else { |
149 | Uint64 add = SDL_rand_bits_r(&rndContext) | ((Uint64) SDL_rand_bits_r(&rndContext) << 32); |
150 | return (Sint32) (min + (Sint64) (add % (range + 1))); |
151 | } |
152 | } |
153 | |
154 | /** |
155 | * Generates a unsigned boundary value between the given boundaries. |
156 | * Boundary values are inclusive. See the examples below. |
157 | * If boundary2 < boundary1, the values are swapped. |
158 | * If boundary1 == boundary2, value of boundary1 will be returned |
159 | * |
160 | * Generating boundary values for Uint8: |
161 | * BoundaryValues(UINT8_MAX, 10, 20, True) -> [10,11,19,20] |
162 | * BoundaryValues(UINT8_MAX, 10, 20, False) -> [9,21] |
163 | * BoundaryValues(UINT8_MAX, 0, 15, True) -> [0, 1, 14, 15] |
164 | * BoundaryValues(UINT8_MAX, 0, 15, False) -> [16] |
165 | * BoundaryValues(UINT8_MAX, 0, 0xFF, False) -> [0], error set |
166 | * |
167 | * Generator works the same for other types of unsigned integers. |
168 | * |
169 | * \param maxValue The biggest value that is acceptable for this data type. |
170 | * For instance, for Uint8 -> 255, Uint16 -> 65536 etc. |
171 | * \param boundary1 defines lower boundary |
172 | * \param boundary2 defines upper boundary |
173 | * \param validDomain Generate only for valid domain (for the data type) |
174 | * |
175 | * \returns Returns a random boundary value for the domain or 0 in case of error |
176 | */ |
177 | static Uint64 SDLTest_GenerateUnsignedBoundaryValues(const Uint64 maxValue, Uint64 boundary1, Uint64 boundary2, bool validDomain) |
178 | { |
179 | Uint64 b1, b2; |
180 | Uint64 delta; |
181 | Uint64 tempBuf[4]; |
182 | Uint8 index; |
183 | |
184 | /* Maybe swap */ |
185 | if (boundary1 > boundary2) { |
186 | b1 = boundary2; |
187 | b2 = boundary1; |
188 | } else { |
189 | b1 = boundary1; |
190 | b2 = boundary2; |
191 | } |
192 | |
193 | index = 0; |
194 | if (validDomain == true) { |
195 | if (b1 == b2) { |
196 | return b1; |
197 | } |
198 | |
199 | /* Generate up to 4 values within bounds */ |
200 | delta = b2 - b1; |
201 | if (delta < 4) { |
202 | do { |
203 | tempBuf[index] = b1 + index; |
204 | index++; |
205 | } while (index < delta); |
206 | } else { |
207 | tempBuf[index] = b1; |
208 | index++; |
209 | tempBuf[index] = b1 + 1; |
210 | index++; |
211 | tempBuf[index] = b2 - 1; |
212 | index++; |
213 | tempBuf[index] = b2; |
214 | index++; |
215 | } |
216 | } else { |
217 | /* Generate up to 2 values outside of bounds */ |
218 | if (b1 > 0) { |
219 | tempBuf[index] = b1 - 1; |
220 | index++; |
221 | } |
222 | |
223 | if (b2 < maxValue) { |
224 | tempBuf[index] = b2 + 1; |
225 | index++; |
226 | } |
227 | } |
228 | |
229 | if (index == 0) { |
230 | /* There are no valid boundaries */ |
231 | SDL_Unsupported(); |
232 | return 0; |
233 | } |
234 | |
235 | return tempBuf[SDLTest_RandomUint8() % index]; |
236 | } |
237 | |
238 | Uint8 SDLTest_RandomUint8BoundaryValue(Uint8 boundary1, Uint8 boundary2, bool validDomain) |
239 | { |
240 | /* max value for Uint8 */ |
241 | const Uint64 maxValue = UCHAR_MAX; |
242 | return (Uint8)SDLTest_GenerateUnsignedBoundaryValues(maxValue, |
243 | (Uint64)boundary1, (Uint64)boundary2, |
244 | validDomain); |
245 | } |
246 | |
247 | Uint16 SDLTest_RandomUint16BoundaryValue(Uint16 boundary1, Uint16 boundary2, bool validDomain) |
248 | { |
249 | /* max value for Uint16 */ |
250 | const Uint64 maxValue = USHRT_MAX; |
251 | return (Uint16)SDLTest_GenerateUnsignedBoundaryValues(maxValue, |
252 | (Uint64)boundary1, (Uint64)boundary2, |
253 | validDomain); |
254 | } |
255 | |
256 | Uint32 SDLTest_RandomUint32BoundaryValue(Uint32 boundary1, Uint32 boundary2, bool validDomain) |
257 | { |
258 | /* max value for Uint32 */ |
259 | #if ((ULONG_MAX) == (UINT_MAX)) |
260 | const Uint64 maxValue = ULONG_MAX; |
261 | #else |
262 | const Uint64 maxValue = UINT_MAX; |
263 | #endif |
264 | return (Uint32)SDLTest_GenerateUnsignedBoundaryValues(maxValue, |
265 | (Uint64)boundary1, (Uint64)boundary2, |
266 | validDomain); |
267 | } |
268 | |
269 | Uint64 SDLTest_RandomUint64BoundaryValue(Uint64 boundary1, Uint64 boundary2, bool validDomain) |
270 | { |
271 | /* max value for Uint64 */ |
272 | const Uint64 maxValue = UINT64_MAX; |
273 | return SDLTest_GenerateUnsignedBoundaryValues(maxValue, |
274 | boundary1, boundary2, |
275 | validDomain); |
276 | } |
277 | |
278 | /** |
279 | * Generates a signed boundary value between the given boundaries. |
280 | * Boundary values are inclusive. See the examples below. |
281 | * If boundary2 < boundary1, the values are swapped. |
282 | * If boundary1 == boundary2, value of boundary1 will be returned |
283 | * |
284 | * Generating boundary values for Sint8: |
285 | * SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -10, 20, True) -> [-10,-9,19,20] |
286 | * SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -10, 20, False) -> [-11,21] |
287 | * SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -30, -15, True) -> [-30, -29, -16, -15] |
288 | * SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -127, 15, False) -> [16] |
289 | * SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -127, 127, False) -> [0], error set |
290 | * |
291 | * Generator works the same for other types of signed integers. |
292 | * |
293 | * \param minValue The smallest value that is acceptable for this data type. |
294 | * For instance, for Uint8 -> -127, etc. |
295 | * \param maxValue The biggest value that is acceptable for this data type. |
296 | * For instance, for Uint8 -> 127, etc. |
297 | * \param boundary1 defines lower boundary |
298 | * \param boundary2 defines upper boundary |
299 | * \param validDomain Generate only for valid domain (for the data type) |
300 | * |
301 | * \returns Returns a random boundary value for the domain or 0 in case of error |
302 | */ |
303 | static Sint64 SDLTest_GenerateSignedBoundaryValues(const Sint64 minValue, const Sint64 maxValue, Sint64 boundary1, Sint64 boundary2, bool validDomain) |
304 | { |
305 | Sint64 b1, b2; |
306 | Sint64 delta; |
307 | Sint64 tempBuf[4]; |
308 | Uint8 index; |
309 | |
310 | /* Maybe swap */ |
311 | if (boundary1 > boundary2) { |
312 | b1 = boundary2; |
313 | b2 = boundary1; |
314 | } else { |
315 | b1 = boundary1; |
316 | b2 = boundary2; |
317 | } |
318 | |
319 | index = 0; |
320 | if (validDomain == true) { |
321 | if (b1 == b2) { |
322 | return b1; |
323 | } |
324 | |
325 | /* Generate up to 4 values within bounds */ |
326 | delta = b2 - b1; |
327 | if (delta < 4) { |
328 | do { |
329 | tempBuf[index] = b1 + index; |
330 | index++; |
331 | } while (index < delta); |
332 | } else { |
333 | tempBuf[index] = b1; |
334 | index++; |
335 | tempBuf[index] = b1 + 1; |
336 | index++; |
337 | tempBuf[index] = b2 - 1; |
338 | index++; |
339 | tempBuf[index] = b2; |
340 | index++; |
341 | } |
342 | } else { |
343 | /* Generate up to 2 values outside of bounds */ |
344 | if (b1 > minValue) { |
345 | tempBuf[index] = b1 - 1; |
346 | index++; |
347 | } |
348 | |
349 | if (b2 < maxValue) { |
350 | tempBuf[index] = b2 + 1; |
351 | index++; |
352 | } |
353 | } |
354 | |
355 | if (index == 0) { |
356 | /* There are no valid boundaries */ |
357 | SDL_Unsupported(); |
358 | return minValue; |
359 | } |
360 | |
361 | return tempBuf[SDLTest_RandomUint8() % index]; |
362 | } |
363 | |
364 | Sint8 SDLTest_RandomSint8BoundaryValue(Sint8 boundary1, Sint8 boundary2, bool validDomain) |
365 | { |
366 | /* min & max values for Sint8 */ |
367 | const Sint64 maxValue = SCHAR_MAX; |
368 | const Sint64 minValue = SCHAR_MIN; |
369 | return (Sint8)SDLTest_GenerateSignedBoundaryValues(minValue, maxValue, |
370 | (Sint64)boundary1, (Sint64)boundary2, |
371 | validDomain); |
372 | } |
373 | |
374 | Sint16 SDLTest_RandomSint16BoundaryValue(Sint16 boundary1, Sint16 boundary2, bool validDomain) |
375 | { |
376 | /* min & max values for Sint16 */ |
377 | const Sint64 maxValue = SHRT_MAX; |
378 | const Sint64 minValue = SHRT_MIN; |
379 | return (Sint16)SDLTest_GenerateSignedBoundaryValues(minValue, maxValue, |
380 | (Sint64)boundary1, (Sint64)boundary2, |
381 | validDomain); |
382 | } |
383 | |
384 | Sint32 SDLTest_RandomSint32BoundaryValue(Sint32 boundary1, Sint32 boundary2, bool validDomain) |
385 | { |
386 | /* min & max values for Sint32 */ |
387 | #if ((ULONG_MAX) == (UINT_MAX)) |
388 | const Sint64 maxValue = LONG_MAX; |
389 | const Sint64 minValue = LONG_MIN; |
390 | #else |
391 | const Sint64 maxValue = INT_MAX; |
392 | const Sint64 minValue = INT_MIN; |
393 | #endif |
394 | return (Sint32)SDLTest_GenerateSignedBoundaryValues(minValue, maxValue, |
395 | (Sint64)boundary1, (Sint64)boundary2, |
396 | validDomain); |
397 | } |
398 | |
399 | Sint64 SDLTest_RandomSint64BoundaryValue(Sint64 boundary1, Sint64 boundary2, bool validDomain) |
400 | { |
401 | /* min & max values for Sint64 */ |
402 | const Sint64 maxValue = INT64_MAX; |
403 | const Sint64 minValue = INT64_MIN; |
404 | return SDLTest_GenerateSignedBoundaryValues(minValue, maxValue, |
405 | boundary1, boundary2, |
406 | validDomain); |
407 | } |
408 | |
409 | float SDLTest_RandomUnitFloat(void) |
410 | { |
411 | return SDL_randf_r(&rndContext); |
412 | } |
413 | |
414 | float SDLTest_RandomFloat(void) |
415 | { |
416 | union |
417 | { |
418 | float f; |
419 | Uint32 v32; |
420 | } value; |
421 | |
422 | do { |
423 | value.v32 = SDLTest_RandomUint32(); |
424 | } while (SDL_isnanf(value.f) || SDL_isinff(value.f)); |
425 | |
426 | return value.f; |
427 | } |
428 | |
429 | double SDLTest_RandomUnitDouble(void) |
430 | { |
431 | return (double)(SDLTest_RandomUint64() >> (64-53)) * 0x1.0p-53; |
432 | } |
433 | |
434 | double SDLTest_RandomDouble(void) |
435 | { |
436 | union |
437 | { |
438 | double d; |
439 | Uint64 v64; |
440 | } value; |
441 | |
442 | do { |
443 | value.v64 = SDLTest_RandomUint64(); |
444 | } while (SDL_isnan(value.d) || SDL_isinf(value.d)); |
445 | |
446 | return value.d; |
447 | } |
448 | |
449 | char *SDLTest_RandomAsciiString(void) |
450 | { |
451 | return SDLTest_RandomAsciiStringWithMaximumLength(255); |
452 | } |
453 | |
454 | char *SDLTest_RandomAsciiStringWithMaximumLength(int maxLength) |
455 | { |
456 | int size; |
457 | |
458 | if (maxLength < 1) { |
459 | SDL_InvalidParamError("maxLength" ); |
460 | return NULL; |
461 | } |
462 | |
463 | size = (SDLTest_RandomUint32() % (maxLength + 1)); |
464 | if (size == 0) { |
465 | size = 1; |
466 | } |
467 | return SDLTest_RandomAsciiStringOfSize(size); |
468 | } |
469 | |
470 | char *SDLTest_RandomAsciiStringOfSize(int size) |
471 | { |
472 | char *string; |
473 | int counter; |
474 | |
475 | if (size < 1) { |
476 | SDL_InvalidParamError("size" ); |
477 | return NULL; |
478 | } |
479 | |
480 | string = (char *)SDL_malloc((size + 1) * sizeof(char)); |
481 | if (!string) { |
482 | return NULL; |
483 | } |
484 | |
485 | for (counter = 0; counter < size; ++counter) { |
486 | string[counter] = (char)SDLTest_RandomIntegerInRange(32, 126); |
487 | } |
488 | |
489 | string[counter] = '\0'; |
490 | |
491 | fuzzerInvocationCounter++; |
492 | |
493 | return string; |
494 | } |
495 | |