BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bsls_fuzztest.h
Go to the documentation of this file.
1/// @file bsls_fuzztest.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bsls_fuzztest.h -*-C++-*-
8#ifndef INCLUDED_BSLS_FUZZTEST
9#define INCLUDED_BSLS_FUZZTEST
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bsls_fuzztest bsls_fuzztest
15/// @brief Provide macros for use in fuzz testing narrow-contract functions.
16/// @addtogroup bsl
17/// @{
18/// @addtogroup bsls
19/// @{
20/// @addtogroup bsls_fuzztest
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bsls_fuzztest-purpose"> Purpose</a>
25/// * <a href="#bsls_fuzztest-classes"> Classes </a>
26/// * <a href="#bsls_fuzztest-macros"> Macros </a>
27/// * <a href="#bsls_fuzztest-description"> Description </a>
28/// * <a href="#bsls_fuzztest-usage"> Usage </a>
29/// * <a href="#bsls_fuzztest-example-basic-usage-of-macros"> Example: Basic Usage of Macros </a>
30///
31/// # Purpose {#bsls_fuzztest-purpose}
32/// Provide macros for use in fuzz testing narrow-contract functions.
33///
34/// # Classes {#bsls_fuzztest-classes}
35///
36/// - bsls::FuzzTestPreconditionTracker: utility for tracking assert violations
37/// - bsls::FuzzTestHandlerGuard: guard for fuzz testing assert-handler
38///
39/// # Macros {#bsls_fuzztest-macros}
40///
41/// - BSLS_FUZZTEST_EVALUATE(EXPRESSION): wrapper for narrow contract function
42/// - BSLS_FUZZTEST_EVALUATE_RAW(EXPRESSION): wrapper with no origination check
43///
44/// @see bsls_preconditions
45///
46/// # Description {#bsls_fuzztest-description}
47/// This component provides two macros, `BSLS_FUZZTEST_EVALUATE`
48/// and `BSLS_FUZZTEST_EVALUATE_RAW`, that can be used in fuzz testing narrow
49/// contract functions. They are intended to be used in conjunction with
50/// `bsls::FuzzTestHandlerGuard` as well as `BSLS_PRECONDITIONS_BEGIN` and
51/// `BSLS_PRECONDITIONS_END`.
52///
53/// When fuzzing narrow contract functions, if we do not wish to "massage" the
54/// data we pass to the function (as this may be error-prone and might introduce
55/// bias into the tested input) we must address the issue that we will often
56/// invoke the function out of contract, and this will cause the function to
57/// assert, and the test to end prematurely. The macros defined in this
58/// component solve this issue by detecting the location of precondition
59/// violations. Functions with narrow contracts that are to be tested must be
60/// decorated with the `BSLS_PRECONDITIONS_BEGIN` and `BSLS_PRECONDITIONS_END`
61/// macros. These macros must be placed just before and after the function
62/// preconditions are checked.
63///
64/// All these macros are intended to be used in fuzzing builds in which
65/// `BDE_ACTIVATE_FUZZ_TESTING` is defined. For our purposes, those
66/// preconditions that fail in the function under test (i.e., the one invoked
67/// by `BSLS_FUZZTEST_EVALUATE`) are treated differently from all other
68/// precondition failures. We refer to these preconditions as "top-level"
69/// preconditions. If a top-level precondition fails -- and the assertion is
70/// not from another component -- the execution will continue: we do not wish to
71/// stop the fuzz test if we simply invoked the narrow contract function under
72/// test out of contract. We wish to detect only subsequent assertions (i.e.,
73/// not in the top-level), or assertions from other components.
74///
75/// The `BSLS_FUZZTEST_EVALUATE_RAW` macro does not check if the assertion
76/// originates from another component, though, like the non-`RAW` version, it
77/// ignores only top-level assertions. This behavior is desirable in cases in
78/// which a function delegates its implementation and associated precondition
79/// checks to a different component. In such cases, a precondition failure
80/// ought not cause the fuzz test to end.
81///
82/// ## Usage {#bsls_fuzztest-usage}
83///
84///
85/// This section illustrates intended use of this component.
86///
87/// ### Example: Basic Usage of Macros {#bsls_fuzztest-example-basic-usage-of-macros}
88///
89///
90/// The macros in this component rely upon the presence of related macros from
91/// @ref bsls_preconditions . The fuzzing macros are typically used in a fuzzing
92/// build, in which case the entry point is `LLVMFuzzerTestOneInput`.
93///
94/// In this example, we illustrate the intended usage of two macros:
95/// `BSLS_FUZZTEST_EVALUATE` and `BSLS_FUZZTEST_EVALUATE_RAW`.
96///
97/// First, in order to illustrate the use of `BSLS_FUZZTEST_EVALUATE`, we
98/// define two functions that implement the `sqrt` function, both decorated
99/// with the precondition `BEGIN` and `END` macros. `mySqrt` forwards its
100/// argument to `newtonsSqrt`, which has a slightly more restrictive
101/// precondition: `mySqrt` accepts 0, while `newtonsSqrt` does not.
102/// @code
103/// double newtonsSqrt(double x)
104/// // Return the square root of the specified 'x' according to Newton's
105/// // method. The behavior is undefined unless 'x > 0'.
106/// {
107/// BSLS_PRECONDITIONS_BEGIN();
108/// BSLS_ASSERT(x > 0);
109/// BSLS_PRECONDITIONS_END();
110///
111/// double guess = 1.0;
112/// for (int ii = 0; ii < 100; ++ii) {
113/// guess = (guess + x / guess) / 2;
114/// }
115/// return guess;
116/// }
117///
118/// double mySqrt(double x)
119/// // Return the square root of the specified 'x'. The behavior is
120/// // undefined unless 'x >= 0'.
121/// {
122/// BSLS_PRECONDITIONS_BEGIN();
123/// BSLS_ASSERT(x >= 0);
124/// BSLS_PRECONDITIONS_END();
125/// return newtonsSqrt(x);
126/// }
127/// @endcode
128/// Then, for the illustration of `BSLS_FUZZTEST_EVALUATE_RAW`, we define a
129/// class, `Timer`, containing a `start` function that uses in its
130/// implementation a narrow contract function, `setInterval`, from another
131/// component, `bsls::TimeInterval`. This function, `setInterval`, has
132/// precondition checks that are surrounded by `BEGIN` and `END`.
133/// @code
134/// class Timer
135/// // This class implements a simple interval timer.
136/// {
137/// private:
138/// // DATA
139/// bsls::TimeInterval d_timeout; // timeout seconds and nanoseconds
140///
141/// public:
142/// // MANIPULATORS
143/// void start(bsls::Types::Int64 seconds, int nanoseconds)
144/// // Start the countdown with a timer having the value given by the
145/// // sum of the specified integral number of 'seconds' and
146/// // 'nanoseconds'. The behavior is undefined unless the total
147/// // number of seconds in the resulting time interval can be
148/// // represented with a 64-bit signed integer (see
149/// // 'TimeInterval::isValid').
150/// {
151/// d_timeout.setInterval(seconds, nanoseconds);
152/// //...
153/// }
154/// };
155/// @endcode
156/// Next, implement `LLVMFuzzerTestOneInput`. We first select the test case
157/// number based on the supplied fuzz data.
158/// @code
159/// extern "C"
160/// int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
161/// // Use the specified 'data' array of 'size' bytes as input to methods
162/// // of this component and return zero.
163/// {
164/// int test;
165/// if (data && size) {
166/// test = static_cast<unsigned char>(*data) % 100;
167/// ++data;
168/// --size;
169/// }
170/// else {
171/// test = 0;
172/// }
173///
174/// switch (test) { case 0: // Zero is always the leading case.
175/// @endcode
176/// Then, we implement the test case to illustrate the use of
177/// `BSLS_FUZZTEST_EVALUATE`.
178/// @code
179/// case 2: {
180/// // ----------------------------------------------------------------
181/// // 'mySqrt'
182/// //
183/// // Concerns:
184/// //: 1. That 'mySqrt' does not invoke the original assertion handler
185/// //: for any 'input' value.
186/// //
187/// // Testing: double mySqrt(double x);
188/// // ----------------------------------------------------------------
189/// if (size < sizeof(double)) {
190/// return 0; // RETURN
191/// }
192/// double input;
193/// memcpy(&input, data, sizeof(double));
194/// @endcode
195/// Next, we set up the handler guard that installs the precondition handlers.
196/// @code
197/// bsls::FuzzTestHandlerGuard hg;
198/// @endcode
199/// Now, we invoke the function under test (i.e., `mySqrt`) with the
200/// `BSLS_FUZZTEST_EVALUATE` macro.
201/// @code
202/// BSLS_FUZZTEST_EVALUATE(mySqrt(input));
203/// @endcode
204/// If the `input` value obtained from the fuzz data is positive (e.g., 4.0),
205/// the `mySqrt` implementation generates correct results without any errors.
206/// For negative inputs (e.g., -4.0), because the precondition violation occurs
207/// in the top level, execution of the test does not halt. If 0 is passed as
208/// the input, `mySqrt` forwards it to `newtonsSqrt` where a second-level
209/// assertion occurs and execution halts, indicating a defect in the
210/// implementation of `mySqrt`.
211/// @code
212/// } break;
213/// @endcode
214/// Next, we implement the test case to illustrate the use of
215/// `BSLS_FUZZTEST_EVALUATE_RAW`.
216/// @code
217/// case 1: {
218/// // ----------------------------------------------------------------
219/// // 'Timer::start'
220/// //
221/// // Concerns:
222/// //: 1 That 'start', when invoked with the 'RAW' macro, does not
223/// //: invoke the original assertion handler.
224/// //
225/// // Testing:
226/// // void Timer::start(Int64 seconds, int nanoseconds);
227/// // ----------------------------------------------------------------
228///
229/// if (size < sizeof(bsls::Types::Int64) + sizeof(int)) {
230/// return 0; // RETURN
231/// }
232/// bsls::Types::Int64 seconds;
233/// int nanoseconds;
234/// memcpy(&seconds, data, sizeof(bsls::Types::Int64));
235/// memcpy(&nanoseconds,
236/// data + sizeof(bsls::Types::Int64),
237/// sizeof(int));
238/// @endcode
239/// Now, we set up the handler guard that installs the precondition handlers.
240/// @code
241/// bsls::FuzzTestHandlerGuard hg;
242/// @endcode
243/// Finally, we invoke the function under test with the
244/// `BSLS_FUZZTEST_EVALUATE_RAW` macro.
245/// @code
246/// Timer t;
247/// BSLS_FUZZTEST_EVALUATE_RAW(t.start(seconds, nanoseconds));
248/// @endcode
249/// If the total number of seconds resulting from the sum of `seconds` and
250/// `nanoseconds` cannot be represented with a 64-bit signed integer, a
251/// top-level assertion failure from a different component will occur. Because
252/// we have invoked `start` with the `RAW` macro, a component name check will
253/// not be performed, and execution will continue.
254/// @code
255/// } break;
256/// default: {
257/// } break;
258/// }
259///
260/// if (testStatus > 0) {
261/// BSLS_ASSERT_INVOKE("FUZZ TEST FAILURES");
262/// }
263///
264/// return 0;
265/// }
266/// @endcode
267/// Note that the use of `bslim::FuzzUtil` and `bslim::FuzzDataView` can
268/// simplify the consumption of fuzz data.
269/// @}
270/** @} */
271/** @} */
272
273/** @addtogroup bsl
274 * @{
275 */
276/** @addtogroup bsls
277 * @{
278 */
279/** @addtogroup bsls_fuzztest
280 * @{
281 */
282
283#include <bsls_assert.h>
285#include <bsls_preconditions.h>
286
287 // =================
288 // Macro Definitions
289 // =================
290
291#if defined(BDE_BUILD_TARGET_EXC)
292#define BSLS_FUZZTEST_EVALUATE_IMP(X) do { \
293 try { \
294 BloombergLP::bsls::FuzzTestPreconditionTracker::initStaticState( \
295 __FILE__); \
296 X; \
297 } \
298 catch (BloombergLP::bsls::FuzzTestPreconditionException& ftpe) { \
299 BloombergLP::bsls::FuzzTestPreconditionTracker::handleException( \
300 ftpe); \
301 } \
302 } while (false)
303
304#define BSLS_FUZZTEST_EVALUATE_RAW_IMP(X) do { \
305 try { \
306 BloombergLP::bsls::FuzzTestPreconditionTracker::initStaticState( \
307 __FILE__); \
308 X; \
309 } \
310 catch (BloombergLP::bsls::FuzzTestPreconditionException& ftpe) { \
311 } \
312 } while (false)
313
314#else
315#define BSLS_FUZZTEST_EVALUATE_IMP(X) do { \
316 X; \
317 } while (false)
318#define BSLS_FUZZTEST_EVALUATE_RAW_IMP(X) do { \
319 X; \
320 } while (false)
321#endif // defined(BDE_BUILD_TARGET_EXC)
322
323#ifdef BDE_ACTIVATE_FUZZ_TESTING
324
325#define BSLS_FUZZTEST_EVALUATE(X) BSLS_FUZZTEST_EVALUATE_IMP(X)
326
327#define BSLS_FUZZTEST_EVALUATE_RAW(X) BSLS_FUZZTEST_EVALUATE_RAW_IMP(X)
328
329#else
330#define BSLS_FUZZTEST_EVALUATE(X) do { \
331 X; \
332 } while (false)
333
334#define BSLS_FUZZTEST_EVALUATE_RAW(X) do { \
335 X; \
336 } while (false)
337
338#endif // defined(BDE_ACTIVATE_FUZZ_TESTING)
339
340namespace bsls {
341
342 // =================================
343 // class FuzzTestPreconditionTracker
344 // =================================
345
346/// This utility class is used by the preprocessor macros to appropriately
347/// handle precondition violations that occur in different levels and
348/// components.
350
351 private:
352 // CLASS DATA
353 static const char *s_file_p; // filename passed to 'initStaticState'
354
355 static bool
356 s_isInFirstPreconditionBlock; // flag indicating whether the first
357 // 'BEGIN/END' block has been closed
358
359 static int s_level; // nesting level of 'BEGIN'/'END' call
360
361 public:
362 // CLASS METHODS
363
364 /// Invoke the assertion handler returned by
365 /// `FuzzTestHandlerGuard::getOriginalAssertionHandler` if the assertion
366 /// violation wrapped by the specified `exception` was encountered in a
367 /// component different from one supplied to `initStaticState`, and do
368 /// nothing otherwise.
369 static void handleException(
370 const FuzzTestPreconditionException& exception);
371
372 /// Throw a `FuzzTestPreconditionException` constructed from the
373 /// specified `violation` if the assertion violation occurred after the
374 /// first invocation of `handlePreconditionsBegin` but before the first
375 /// invocation of `handlePreconditionsEnd`, and invoke the assertion
376 /// handler returned by
377 /// `FuzzTestHandlerGuard::getOriginalAssertionHandler` otherwise.
378 static void handlePreconditionViolation(const AssertViolation& violation);
379
380 /// Increment the assertion block depth level counter.
382
383 /// Decrement the assertion block depth level counter and record that
384 /// the first precondition block has ended if the depth level changed to
385 /// 0. The behavior is undefined unless the depth level is positive.
387
388 /// Store the specified `fileName` from the caller that invokes the
389 /// top-level function under test (via `BSLS_FUZZTEST_EVALUATE(X)`), and
390 /// set the state to reflect that any precondition begin macro
391 /// encountered will be the first.
392 static void initStaticState(const char *fileName);
393};
394
395 // ==========================
396 // class FuzzTestHandlerGuard
397 // ==========================
398
399/// This class provides a guard that will install and uninstall three
400/// handlers, one for assertion failure, one for `BSLS_PRECONDITIONS_BEGIN`,
401/// and one for `BSLS_PRECONDITIONS_END`, within the protected scope.
402///
403/// See @ref bsls_fuzztest
405
406 private:
407 // CLASS DATA
409 s_originalAssertionHandler; // original assertion handler
410
411 public:
412 // CREATORS
413
414 /// Create a guard object, installing
415 /// `FuzzTestPreconditionTracker::handlePreconditionViolation` as well
416 /// as the `BEGIN/END` handler. The behavior is undefined if the
417 /// current assertion handler is
418 /// `FuzzTestPreconditionTracker::handlePreconditionViolation`.
420
421 /// Restore the failure handler that was in place when this object was
422 /// created, reset the precondition `BEGIN/END` handlers to no-op, and
423 /// destroy this guard.
425
426 // CLASS METHODS
427
428 /// Return the original assertion handler.
430};
431
432// ============================================================================
433// INLINE DEFINITIONS
434// ============================================================================
435
436inline
451
452inline
459
460inline
462{
463 return s_originalAssertionHandler;
464}
465
466} // close package namespace
467
468
469#endif
470
471// ----------------------------------------------------------------------------
472// Copyright 2021 Bloomberg Finance L.P.
473//
474// Licensed under the Apache License, Version 2.0 (the "License");
475// you may not use this file except in compliance with the License.
476// You may obtain a copy of the License at
477//
478// http://www.apache.org/licenses/LICENSE-2.0
479//
480// Unless required by applicable law or agreed to in writing, software
481// distributed under the License is distributed on an "AS IS" BASIS,
482// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
483// See the License for the specific language governing permissions and
484// limitations under the License.
485// ----------------------------- END-OF-FILE ----------------------------------
486
487/** @} */
488/** @} */
489/** @} */
Definition bsls_assert.h:1929
static Assert::ViolationHandler violationHandler()
static void setViolationHandler(Assert::ViolationHandler function)
void(* ViolationHandler)(const AssertViolation &)
Definition bsls_assert.h:2008
Definition bsls_fuzztest.h:404
FuzzTestHandlerGuard()
Definition bsls_fuzztest.h:437
~FuzzTestHandlerGuard()
Definition bsls_fuzztest.h:453
static Assert::ViolationHandler getOriginalAssertionHandler()
Return the original assertion handler.
Definition bsls_fuzztest.h:461
Definition bsls_fuzztestpreconditionexception.h:118
static void installHandlers(PreconditionHandlerType beginHandler, PreconditionHandlerType endHandler)
static void noOpHandler()
Do nothing.
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1804
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition bdlt_iso8601util.h:691
Definition bsls_fuzztest.h:349
static void handleException(const FuzzTestPreconditionException &exception)
static void handlePreconditionViolation(const AssertViolation &violation)
static void initStaticState(const char *fileName)
static void handlePreconditionsBegin()
Increment the assertion block depth level counter.