BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bslim_fuzzutil.h
Go to the documentation of this file.
1/// @file bslim_fuzzutil.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslim_fuzzutil.h -*-C++-*-
8#ifndef INCLUDED_BSLIM_FUZZUTIL
9#define INCLUDED_BSLIM_FUZZUTIL
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bslim_fuzzutil bslim_fuzzutil
15/// @brief Provide fuzz test utilities for basic types.
16/// @addtogroup bsl
17/// @{
18/// @addtogroup bslim
19/// @{
20/// @addtogroup bslim_fuzzutil
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bslim_fuzzutil-purpose"> Purpose</a>
25/// * <a href="#bslim_fuzzutil-classes"> Classes </a>
26/// * <a href="#bslim_fuzzutil-description"> Description </a>
27/// * <a href="#bslim_fuzzutil-usage"> Usage </a>
28/// * <a href="#bslim_fuzzutil-example-1-consuming-integers-in-a-range-to-pass-to-an-interface"> Example 1: Consuming Integers in a Range to Pass to an Interface </a>
29///
30/// # Purpose {#bslim_fuzzutil-purpose}
31/// Provide fuzz test utilities for basic types.
32///
33/// # Classes {#bslim_fuzzutil-classes}
34///
35/// - bslim::FuzzUtil: functions to create basic types from fuzz data
36///
37/// @see bslim_fuzzdataview
38///
39/// # Description {#bslim_fuzzutil-description}
40/// This component provides a namespace, `bslim::FuzzUtil`,
41/// containing functions that create fundamental and standard library types from
42/// fuzz data provided by a fuzz harness (e.g., `libFuzzer`).
43///
44/// See {http://bburl/BDEFuzzTesting} for details on how to build and run with
45/// fuzz testing enabled.
46///
47/// ## Usage {#bslim_fuzzutil-usage}
48///
49///
50/// This section illustrates intended use of this component.
51///
52/// ### Example 1: Consuming Integers in a Range to Pass to an Interface {#bslim_fuzzutil-example-1-consuming-integers-in-a-range-to-pass-to-an-interface}
53///
54///
55/// Suppose we wish to fuzz test a function with preconditions.
56///
57/// First, we define the `TradingInterfaceUnderTest` `struct`:
58/// @code
59/// /// This utility class provides sample functionality to demonstrate how fuzz
60/// /// data might be used.
61/// struct TradingInterfaceUnderTest {
62/// // CLASS METHODS
63///
64/// /// Return a value containing the number of earnings announcements in
65/// /// the specified `year` and `month`. The behavior is undefined unless
66/// /// `1950 < year < 2030` and `month` is in `[1 .. 12]`. Note that the
67/// /// values here are arbitrary, and in the real-world this data would be
68/// /// obtained from a database or an API.
69/// static int numEarningsAnnouncements(int year, int month)
70/// {
71/// BSLS_ASSERT(1950 < year && year < 2030);
72/// BSLS_ASSERT( 1 <= month && month <= 12);
73///
74/// if (2020 < year && 6 < month) {
75/// return 11; // RETURN
76/// }
77/// return 6;
78/// }
79/// };
80/// @endcode
81/// Then, we need a block of raw bytes. This would normally come from a fuzz
82/// harness (e.g., the `LLVMFuzzerTestOneInput` entry point function from
83/// `libFuzzer`). Since `libFuzzer` is not available here, we initialize a
84/// `myFuzzData` array that we will use instead.
85/// @code
86/// const bsl::uint8_t myFuzzData[] = {0x43, 0x19, 0x0D, 0x44, 0x37, 0x0D,
87/// 0x38, 0x5E, 0x9B, 0xAA, 0xF3, 0xDA};
88/// @endcode
89/// Next, we create a `FuzzDataView` to wrap the raw bytes.
90/// @code
91/// bslim::FuzzDataView fdv(myFuzzData, sizeof myFuzzData);
92/// @endcode
93/// Now, we pass this `FuzzDataView` to `FuzzUtil` to generate values within the
94/// permissible range of the function under test:
95/// @code
96/// int month = bslim::FuzzUtil::consumeNumberInRange<int>(&fdv, 1, 12);
97/// int year = bslim::FuzzUtil::consumeNumberInRange<int>(&fdv, 1951, 2029);
98/// assert( 1 <= month && month <= 12);
99/// assert(1951 <= year && year <= 2029);
100/// @endcode
101/// Finally, we can use these `int` values to pass to a function that returns
102/// the number of earnings announcements scheduled in a given month.
103/// @code
104/// int numEarnings =
105/// TradingInterfaceUnderTest::numEarningsAnnouncements(year, month);
106/// (void) numEarnings;
107/// @endcode
108/// @}
109/** @} */
110/** @} */
111
112/** @addtogroup bsl
113 * @{
114 */
115/** @addtogroup bslim
116 * @{
117 */
118/** @addtogroup bslim_fuzzutil
119 * @{
120 */
121
122#include <bslscm_version.h>
123
124#include <bslim_fuzzdataview.h>
125
126#include <bslmf_assert.h>
127
128#include <bsls_assert.h>
129#include <bsls_libraryfeatures.h>
130#include <bsls_types.h> // `bsls::Types::Uint64`
131
132#include <bsl_cmath.h> // `bsl::isfinite`
133#include <bsl_cstdint.h> // `bsl::uint8_t`
134#include <bsl_limits.h> // `bsl::numeric_limits`
135#include <bsl_string.h>
136#include <bsl_type_traits.h> // `bsl::is_same`
137#include <bsl_vector.h>
138
139#include <string>
140#include <vector>
141
142
143namespace bslim {
144
145 // ===============
146 // struct FuzzUtil
147 // ===============
148
149/// This utility `struct` provides a namespace for a suite of functions
150/// operating on objects of type `FuzzDataView`and providing the consumption
151/// of fuzz data bytes into fundamental and standard library types.
152///
153/// See @ref bslim_fuzzutil
154struct FuzzUtil {
155
156 // CLASS METHODS
157
158 /// Return a `bool` value based upon consuming a single byte from the
159 /// specified `fuzzDataView`. If `fuzzDataView->length()` is 0, return
160 /// `false`.
161 static bool consumeBool(FuzzDataView *fuzzDataView);
162
163 template <class TYPE>
164 static typename bsl::enable_if<bsl::is_integral<TYPE>::value, TYPE>::type
165 consumeNumber(FuzzDataView *fuzzDataView);
166
167 /// Return a value of (template parameter) `TYPE` in the range
168 /// [min .. max] -- where `min` and `max` are the minimum and maximum
169 /// values representable by the `TYPE` -- based on at most the next
170 /// `sizeof(TYPE) + 1` bytes from the specified `fuzzDataView`, and
171 /// update `fuzzDataView` to reflect the bytes consumed. If
172 /// `0 == fuzzDataView->length()`, return the minimum value of `TYPE`.
173 /// This function does not participate in overload resolution unless
174 /// either `bsl::is_integral<TYPE>::value` or
175 /// `bsl::is_floating_point<TYPE>::value` is `true`.
176 ///
177 /// \pre The behavior is undefined if `bsl::is_same<TYPE, bool>::value` or
178 /// `bsl::is_same<TYPE, long double>` is `true`.
179 template <class TYPE>
180 static typename
183
184 /// Return a value of (template parameter) `TYPE` in the specified range
185 /// [min .. max] based on at most the next `sizeof(TYPE) + 1` bytes from
186 /// the specified `fuzzDataView`, and update `fuzzDataView` to reflect
187 /// the bytes consumed. If `0 == fuzzDataView->length()`, return the
188 /// specified `min`. This function does not participate in overload
189 /// resolution unless either `bsl::is_integral<TYPE>::value` or
190 /// `bsl::is_floating_point<TYPE>::value` is `true`.
191 ///
192 /// \pre The behavior is undefined if `min > max`, `min` or `max` is not finite, or either
193 /// `bsl::is_same<TYPE, bool>::value` or
194 /// `bsl::is_same<TYPE, long double>` is `true`.
195 template <class TYPE>
196 static typename
199 TYPE min,
200 TYPE max);
201 template <class TYPE>
202 static typename
204 consumeNumberInRange(FuzzDataView *fuzzDataView, TYPE min, TYPE max);
205
206 /// Load into the specified `output` a string consisting of printable ASCII
207 /// characters of length from 0 to the specified `maxLength`. If the
208 /// specified `fuzzDataView` has fewer bytes than `maxLength`, load at most
209 /// `fuzzDataView->length()` bytes into `output`. If the buffer in
210 /// `fuzzDataView` contains two successive backslash characters, then in
211 /// `output` they will be converted to a single backslash (`\\`) character;
212 /// if a single backslash character is encountered, the consumption of bytes is terminated.
213 ///
214 /// \note Note that because double backslashes are mapped
215 /// to single backslashes, more than `maxLength` bytes may be consumed from
216 /// the buffer to produce the `output`. Also note that the generated
217 /// string is also a valid UTF-8 string.
219 FuzzDataView *fuzzDataView,
220 bsl::size_t maxLength);
221 static void consumeRandomLengthAsciiString(std::string *output,
222 FuzzDataView *fuzzDataView,
223 bsl::size_t maxLength);
224#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR_STRING
225 static void consumeRandomLengthAsciiString(std::pmr::string *output,
226 FuzzDataView *fuzzDataView,
227 bsl::size_t maxLength);
228#endif
229
230 /// Load into the specified `output` a sequence of characters of length
231 /// from 0 to the specified `maxLength`. If the specified `fuzzDataView`
232 /// has fewer bytes than `maxLength`, load at most `fuzzDataView->length()`
233 /// bytes into `output`. If the buffer in `fuzzDataView` contains two
234 /// successive backslash characters, then in `output` they will be
235 /// converted to a single backslash (`\\`) character; if a single backslash
236 /// character is encountered, the consumption of bytes is terminated.
237 ///
238 /// \note Note that because double backslashes are mapped to single backslashes, more
239 /// than `maxLength` bytes may be consumed from the buffer to produce the
240 /// `output`. Also note that the purpose of this function is to enable the
241 /// creation of a non-zero-terminated @ref string_view , which is not possible
242 /// with the `string` counterpart.
244 FuzzDataView *fuzzDataView,
245 bsl::size_t maxLength);
246 static void consumeRandomLengthChars(std::vector<char> *output,
247 FuzzDataView *fuzzDataView,
248 bsl::size_t maxLength);
249#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
250 static void consumeRandomLengthChars(std::pmr::vector<char> *output,
251 FuzzDataView *fuzzDataView,
252 bsl::size_t maxLength);
253#endif
254
255 /// Load into the specified `output` a string of length from 0 to the
256 /// specified `maxLength`. If the specified `fuzzDataView` has fewer bytes
257 /// than `maxLength`, load at most `fuzzDataView->length()` bytes into
258 /// `output`. If the buffer in `fuzzDataView` contains two successive
259 /// backslash characters, then in `output` they will be converted to a
260 /// single backslash (`\\`) character; if a single backslash character is encountered, the consumption of bytes is terminated.
261 ///
262 /// \note Note that because
263 /// double backslashes are mapped to single backslashes, more than
264 /// `maxLength` bytes may be consumed from the buffer to produce the
265 /// `output`.
267 FuzzDataView *fuzzDataView,
268 bsl::size_t maxLength);
269 static void consumeRandomLengthString(std::string *output,
270 FuzzDataView *fuzzDataView,
271 bsl::size_t maxLength);
272#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR_STRING
273 static void consumeRandomLengthString(std::pmr::string *output,
274 FuzzDataView *fuzzDataView,
275 bsl::size_t maxLength);
276#endif
277};
278
279// ============================================================================
280// INLINE DEFINITIONS
281// ============================================================================
282
283 // ---------------
284 // struct FuzzUtil
285 // ---------------
286
287// CLASS METHODS
288inline
290{
291 return 1 & consumeNumber<bsl::uint8_t>(fuzzDataView);
292}
293
294template <class TYPE>
297{
298 return consumeNumberInRange(fuzzDataView,
299 bsl::numeric_limits<TYPE>::min(),
300 bsl::numeric_limits<TYPE>::max());
301}
302
303template <class TYPE>
304typename
307{
308 return consumeNumberInRange(fuzzDataView,
309 -bsl::numeric_limits<TYPE>::max(),
310 bsl::numeric_limits<TYPE>::max());
311}
312
313template <class TYPE>
315FuzzUtil::consumeNumberInRange(FuzzDataView *fuzzDataView, TYPE min, TYPE max)
316{
319 BSLMF_ASSERT(sizeof(TYPE) <= sizeof(bsls::Types::Uint64));
320 BSLS_ASSERT(min <= max);
321
322 bsls::Types::Uint64 range = static_cast<bsls::Types::Uint64>(max) - min;
323
324 int numBytes = 0;
325
326 for (bsls::Types::Uint64 rangeCpy = range; 0 != rangeCpy;
327 rangeCpy >>= 8, ++numBytes) {
328 }
329
330 bsls::Types::Uint64 addend = 0;
331
332 FuzzDataView prefix = fuzzDataView->removePrefix(numBytes);
333
334 for (const bsl::uint8_t *it = prefix.begin(); it != prefix.end(); it++) {
335 addend = (addend << 8) | *it;
336 }
337
338 if (bsl::numeric_limits<bsls::Types::Uint64>::max() != range) {
339 addend %= (range + 1);
340 }
341
342 return static_cast<TYPE>(min + addend);
343}
344
345template <class TYPE>
347FuzzUtil::consumeNumberInRange(FuzzDataView *fuzzDataView, TYPE min, TYPE max)
348{
350 BSLMF_ASSERT(bsl::numeric_limits<TYPE>::has_infinity);
351
352 BSLS_ASSERT(min <= max);
353
354 BSLS_ASSERT(min == min && max == max);
355 BSLS_ASSERT(bsl::numeric_limits<TYPE>::infinity() != max &&
356 -bsl::numeric_limits<TYPE>::infinity() != min);
357
358 TYPE addend = min;
359 TYPE range = 0;
360 const TYPE k_HALF = 0.5;
361
362 if (max > min + bsl::numeric_limits<TYPE>::max()) {
363 range = max * k_HALF - min * k_HALF;
364 if (consumeBool(fuzzDataView)) {
365 addend = min + range;
366 }
367 }
368 else {
369 range = max - min;
370 }
371
372 typedef typename bsl::conditional<(sizeof(TYPE) <= sizeof(bsl::uint32_t)),
373 bsl::uint32_t,
374 bsls::Types::Uint64>::type IntegralType;
375
376 TYPE factor =
377 static_cast<TYPE>(consumeNumber<IntegralType>(fuzzDataView)) /
378 static_cast<TYPE>(
379 bsl::numeric_limits<IntegralType>::max()); // between 0-1
380
381 return addend + range * factor;
382}
383
384} // close package namespace
385
386
387#endif
388
389// ----------------------------------------------------------------------------
390// Copyright 2021 Bloomberg Finance L.P.
391//
392// Licensed under the Apache License, Version 2.0 (the "License");
393// you may not use this file except in compliance with the License.
394// You may obtain a copy of the License at
395//
396// http://www.apache.org/licenses/LICENSE-2.0
397//
398// Unless required by applicable law or agreed to in writing, software
399// distributed under the License is distributed on an "AS IS" BASIS,
400// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
401// See the License for the specific language governing permissions and
402// limitations under the License.
403// ----------------------------- END-OF-FILE ----------------------------------
404
405/** @} */
406/** @} */
407/** @} */
Definition bslstl_string.h:1252
Definition bslstl_vector.h:1120
Definition bslim_fuzzdataview.h:130
const bsl::uint8_t * end() const
Return a const pointer to the end of the buffer.
Definition bslim_fuzzdataview.h:238
const bsl::uint8_t * begin() const
Return a const pointer to the beginning of the buffer.
Definition bslim_fuzzdataview.h:232
FuzzDataView removePrefix(bsl::size_t numBytes)
Definition bslim_fuzzdataview.h:209
#define BSLMF_ASSERT(expr)
Definition bslmf_assert.h:231
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1976
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition bslim_formatguard.h:120
Definition bslmf_conditional.h:123
Definition bslmf_enableif.h:530
Definition bslmf_isintegral.h:140
Definition bslmf_issame.h:146
Definition bslim_fuzzutil.h:154
static void consumeRandomLengthChars(bsl::vector< char > *output, FuzzDataView *fuzzDataView, bsl::size_t maxLength)
static void consumeRandomLengthAsciiString(bsl::string *output, FuzzDataView *fuzzDataView, bsl::size_t maxLength)
static void consumeRandomLengthString(std::string *output, FuzzDataView *fuzzDataView, bsl::size_t maxLength)
static void consumeRandomLengthAsciiString(std::string *output, FuzzDataView *fuzzDataView, bsl::size_t maxLength)
static bool consumeBool(FuzzDataView *fuzzDataView)
Definition bslim_fuzzutil.h:289
static bsl::enable_if< bsl::is_integral< TYPE >::value, TYPE >::type consumeNumber(FuzzDataView *fuzzDataView)
Definition bslim_fuzzutil.h:296
static bsl::enable_if< bsl::is_integral< TYPE >::value, TYPE >::type consumeNumberInRange(FuzzDataView *fuzzDataView, TYPE min, TYPE max)
Definition bslim_fuzzutil.h:315
static void consumeRandomLengthChars(std::vector< char > *output, FuzzDataView *fuzzDataView, bsl::size_t maxLength)
static void consumeRandomLengthString(bsl::string *output, FuzzDataView *fuzzDataView, bsl::size_t maxLength)
static bsl::enable_if< bsl::is_floating_point< TYPE >::value, TYPE >::type consumeNumberInRange(FuzzDataView *fuzzDataView, TYPE min, TYPE max)
static bsl::enable_if< bsl::is_floating_point< TYPE >::value, TYPE >::type consumeNumber(FuzzDataView *fuzzDataView)
unsigned long long Uint64
Definition bsls_types.h:139