BDE 4.39.x 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-/review-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/review, 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/// whose 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 by
67/// `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
70/// assertion/review is not from another component -- the execution will
71/// continue: we do not wish to stop the fuzz test if we simply invoked the
72/// narrow contract function under test out of contract. We wish to detect only
73/// subsequent assertions/reviews (i.e., not in the top-level), or
74/// assertions/reviews from other components.
75///
76/// The `BSLS_FUZZTEST_EVALUATE_RAW` macro does not check if the
77/// assertion/review originates from another component, though, like the
78/// non-`RAW` version, it ignores only top-level assertions/reviews. This
79/// behavior is desirable in cases in which a function delegates its
80/// implementation and associated precondition checks to a different component.
81/// In such cases, a precondition failure ought not cause the fuzz test to end.
82///
83/// ## Usage {#bsls_fuzztest-usage}
84///
85///
86/// This section illustrates intended use of this component.
87///
88/// ### Example: Basic Usage of Macros {#bsls_fuzztest-example-basic-usage-of-macros}
89///
90///
91/// The macros in this component rely upon the presence of related macros from
92/// @ref bsls_preconditions . The fuzzing macros are typically used in a fuzzing
93/// build, in which case the entry point is `LLVMFuzzerTestOneInput`.
94///
95/// In this example, we illustrate the intended usage of two macros:
96/// `BSLS_FUZZTEST_EVALUATE` and `BSLS_FUZZTEST_EVALUATE_RAW`.
97///
98/// First, in order to illustrate the use of `BSLS_FUZZTEST_EVALUATE`, we define
99/// two functions that implement the `sqrt` function, both decorated with the
100/// precondition `BEGIN` and `END` macros. `mySqrt` forwards its argument to
101/// `newtonsSqrt`, which has a slightly more restrictive precondition: `mySqrt`
102/// accepts 0, while `newtonsSqrt` does not.
103/// @code
104/// /// Return the square root of the specified `x` according to Newton's
105/// /// method. The behavior is undefined unless `x > 0`.
106/// /// Return the square root of the specified `x` according to Newton's
107/// /// method. The behavior is undefined unless `x > 0`.
108/// double newtonsSqrt(double x)
109/// {
110/// BSLS_PRECONDITIONS_BEGIN();
111/// BSLS_ASSERT(x > 0);
112/// BSLS_PRECONDITIONS_END();
113///
114/// double guess = 1.0;
115/// for (int ii = 0; ii < 100; ++ii) {
116/// guess = (guess + x / guess) / 2;
117/// }
118/// return guess;
119/// }
120///
121/// /// Return the square root of the specified `x`. The behavior is undefined
122/// /// unless `x >= 0`.
123/// /// Return the square root of the specified `x`. The behavior is undefined
124/// /// unless `x >= 0`.
125/// double mySqrt(double x)
126/// {
127/// BSLS_PRECONDITIONS_BEGIN();
128/// BSLS_ASSERT(x >= 0);
129/// BSLS_PRECONDITIONS_END();
130/// return newtonsSqrt(x);
131/// }
132/// @endcode
133/// Then, for the illustration of `BSLS_FUZZTEST_EVALUATE_RAW`, we define a
134/// narrow function that uses a narrow function, `triggerAssert`, from another
135/// component, `bsls::FuzzTest_TestUtil`. This function, `triggerAssert`,
136/// always triggers an assertion failure.
137/// @code
138/// /// Invoke `triggerAssert` from `bsls::FuzzTest_TestUtil`. The behavior is
139/// /// undefined if `triggerAssert` fails.
140/// void invokeTriggerAssert()
141/// {
142/// bsls::FuzzTest_TestUtil::triggerAssert();
143/// //...
144/// }
145/// @endcode
146/// Next, implement `LLVMFuzzerTestOneInput`. We first select the test case
147/// number based on the supplied fuzz data.
148/// @code
149/// /// Use the specified `data` array of `size` bytes as input to methods of
150/// /// this component and return zero.
151/// extern "C"
152/// int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
153/// {
154/// int test;
155/// if (data && size) {
156/// test = static_cast<unsigned char>(*data) % 100;
157/// ++data;
158/// --size;
159/// }
160/// else {
161/// test = 0;
162/// }
163///
164/// switch (test) { case 0: // Zero is always the leading case.
165/// @endcode
166/// Then, we implement the test case to illustrate the use of
167/// `BSLS_FUZZTEST_EVALUATE`.
168/// @code
169/// case 2: {
170/// // ----------------------------------------------------------------
171/// // `mySqrt`
172/// //
173/// // Concerns:
174/// // 1. That `mySqrt` does not invoke the original assertion handler
175/// // for any `input` value.
176/// //
177/// // Testing: double mySqrt(double x);
178/// // ----------------------------------------------------------------
179/// if (size < sizeof(double)) {
180/// return 0; // RETURN
181/// }
182/// double input;
183/// memcpy(&input, data, sizeof(double));
184/// @endcode
185/// Next, we set up the handler guard that installs the precondition handlers.
186/// @code
187/// bsls::FuzzTestHandlerGuard hg;
188/// @endcode
189/// Now, we invoke the function under test (i.e., `mySqrt`) with the
190/// `BSLS_FUZZTEST_EVALUATE` macro.
191/// @code
192/// BSLS_FUZZTEST_EVALUATE(mySqrt(input));
193/// @endcode
194/// If the `input` value obtained from the fuzz data is positive (e.g., 4.0),
195/// the `mySqrt` implementation generates correct results without any errors.
196/// For negative inputs (e.g., -4.0), because the precondition violation occurs
197/// in the top level, execution of the test does not halt. If 0 is passed as
198/// the input, `mySqrt` forwards it to `newtonsSqrt` where a second-level
199/// assertion occurs and execution halts, indicating a defect in the
200/// implementation of `mySqrt`.
201/// @code
202/// } break;
203/// @endcode
204/// Next, we implement the test case to illustrate the use of
205/// `BSLS_FUZZTEST_EVALUATE_RAW`.
206/// @code
207/// case 1: {
208/// // ----------------------------------------------------------------
209/// // `invokeTriggerAssert`
210/// //
211/// // Concerns:
212/// // 1. That `invokeTriggerAssert`, when invoked with the `RAW`
213/// // macro, does not invoke the original assertion handler.
214/// //
215/// // Testing: void invokeTriggerAssert();
216/// // ----------------------------------------------------------------
217/// @endcode
218/// Now, we set up the handler guard that installs the precondition handlers.
219/// @code
220/// bsls::FuzzTestHandlerGuard hg;
221/// @endcode
222/// Finally, we invoke the function under test with the
223/// `BSLS_FUZZTEST_EVALUATE_RAW` macro.
224/// @code
225/// BSLS_FUZZTEST_EVALUATE_RAW(invokeTriggerAssert());
226/// @endcode
227/// Here a top-level assertion failure from a different component will occur.
228/// Because we have invoked `invokeTriggerAssert` with the `RAW` macro, a
229/// component name check will not be performed, and execution will continue.
230/// @code
231/// } break;
232/// default: {
233/// } break;
234/// }
235///
236/// if (testStatus > 0) {
237/// BSLS_REVIEW_INVOKE("FUZZ TEST FAILURES");
238/// }
239///
240/// return 0;
241/// }
242/// @endcode
243/// Note that the use of `bslim::FuzzUtil` and `bslim::FuzzDataView` can
244/// simplify the consumption of fuzz data.
245/// @}
246/** @} */
247/** @} */
248
249/** @addtogroup bsl
250 * @{
251 */
252/** @addtogroup bsls
253 * @{
254 */
255/** @addtogroup bsls_fuzztest
256 * @{
257 */
258
259#include <bsls_assert.h>
261#include <bsls_preconditions.h>
262#include <bsls_review.h>
263
264 // =================
265 // Macro Definitions
266 // =================
267
268#if defined(BDE_BUILD_TARGET_EXC)
269#define BSLS_FUZZTEST_EVALUATE_IMP(X) do { \
270 try { \
271 BloombergLP::bsls::FuzzTestPreconditionTracker::initStaticState( \
272 __FILE__); \
273 X; \
274 } \
275 catch (BloombergLP::bsls::FuzzTestPreconditionException& ftpe) { \
276 BloombergLP::bsls::FuzzTestPreconditionTracker::handleException( \
277 ftpe); \
278 } \
279 } while (false)
280
281#define BSLS_FUZZTEST_EVALUATE_RAW_IMP(X) do { \
282 try { \
283 BloombergLP::bsls::FuzzTestPreconditionTracker::initStaticState( \
284 __FILE__); \
285 X; \
286 } \
287 catch (BloombergLP::bsls::FuzzTestPreconditionException& ftpe) { \
288 } \
289 } while (false)
290
291#else
292#define BSLS_FUZZTEST_EVALUATE_IMP(X) do { \
293 X; \
294 } while (false)
295#define BSLS_FUZZTEST_EVALUATE_RAW_IMP(X) do { \
296 X; \
297 } while (false)
298#endif // defined(BDE_BUILD_TARGET_EXC)
299
300#ifdef BDE_ACTIVATE_FUZZ_TESTING
301
302#define BSLS_FUZZTEST_EVALUATE(X) BSLS_FUZZTEST_EVALUATE_IMP(X)
303
304#define BSLS_FUZZTEST_EVALUATE_RAW(X) BSLS_FUZZTEST_EVALUATE_RAW_IMP(X)
305
306#else
307#define BSLS_FUZZTEST_EVALUATE(X) do { \
308 X; \
309 } while (false)
310
311#define BSLS_FUZZTEST_EVALUATE_RAW(X) do { \
312 X; \
313 } while (false)
314
315#endif // defined(BDE_ACTIVATE_FUZZ_TESTING)
316
317
318namespace bsls {
319
320 // =================================
321 // class FuzzTestPreconditionTracker
322 // =================================
323
324/// This utility class is used by the preprocessor macros to appropriately
325/// handle precondition violations that occur in different levels and
326/// components.
327///
328/// See @ref bsls_fuzztest
330
331 private:
332 // CLASS DATA
333 static const char *s_file_p; // filename passed to `initStaticState`
334
335 static bool
336 s_isInFirstPreconditionBlock; // flag indicating whether the first
337 // `BEGIN/END` block has been closed
338
339 static int s_level; // nesting level of `BEGIN`/`END` call
340
341 public:
342 // CLASS METHODS
343
344 /// Throw a `FuzzTestPreconditionException` constructed from the specified
345 /// `violation` if the assertion violation occurred after the first
346 /// invocation of `handlePreconditionsBegin` but before the first
347 /// invocation of `handlePreconditionsEnd`, and invoke the assertion
348 /// handler returned by `FuzzTestHandlerGuard::getOriginalAssertionHandler`
349 /// otherwise.
350 static void handleAssertViolation(const AssertViolation& violation);
351
352 /// Invoke the assertion/review handler returned by
353 /// `FuzzTestHandlerGuard::getOriginalAssertionHandler` or
354 /// `FuzzTestHandlerGuard::getOriginalReviewHandler` if the
355 /// assertion/review violation wrapped by the specified `exception` was
356 /// encountered in a component different from one supplied to
357 /// `initStaticState`, and do nothing otherwise.
358 static void handleException(
359 const FuzzTestPreconditionException& exception);
360
361 /// Increment the assertion/review block depth level counter.
363
364 /// Decrement the assertion/review block depth level counter and record
365 /// that the first precondition block has ended if the depth level changed to 0.
366 ///
367 /// \pre The behavior is undefined unless the depth level is positive.
369
370 /// Throw a `FuzzTestPreconditionException` constructed from the specified
371 /// `violation` if the review violation occurred after the first invocation
372 /// of `handlePreconditionsBegin` but before the first invocation of
373 /// `handlePreconditionsEnd`, and invoke the assertion handler (not review
374 /// handler) returned by
375 /// `FuzzTestHandlerGuard::getOriginalAssertionHandler` otherwise.
376 static void handleReviewViolation(const ReviewViolation& violation);
377
378 /// Store the specified `fileName` from the caller that invokes the
379 /// top-level function under test (via `BSLS_FUZZTEST_EVALUATE(X)`), and
380 /// set the state to reflect that any precondition begin macro encountered
381 /// will be the first.
382 static void initStaticState(const char *fileName);
383};
384
385 // ==========================
386 // class FuzzTestHandlerGuard
387 // ==========================
388
389/// This class provides a guard that will install and uninstall four handlers,
390/// one for assertion failure, one for review failure, one for
391/// `BSLS_PRECONDITIONS_BEGIN`, and one for `BSLS_PRECONDITIONS_END`, within
392/// its protected scope.
393///
394/// See @ref bsls_fuzztest
396
397 private:
398 // CLASS DATA
400 *s_currentFuzzTestHandlerGuard_p; // current fuzz test handler guard
401 // (owned)
402
403 // DATA
405 originalAssertionHandler; // original assertion handler
406
408 originalReviewHandler; // original review handler
409
410 public:
411 // CREATORS
412
413 /// Create a guard object, installing
414 /// `FuzzTestPreconditionTracker::handleAssertViolation`,
415 /// `FuzzTestPreconditionTracker::handleReviewViolation`, and the `BEGIN/END` handler.
416 ///
417 /// \pre The behavior is undefined if the current
418 /// assertion handler is
419 /// `FuzzTestPreconditionTracker::handleAssertViolation` or the current
420 /// review handler is `FuzzTestPreconditionTracker::handleAssertViolation`.
422
423 /// Restore the failure handler that was in place when this object was
424 /// created, reset the precondition `BEGIN/END` handlers to no-op, and destroy this guard.
425 ///
426 /// \pre The behavior is undefined unless the current
427 /// assertion handler is
428 /// `FuzzTestPreconditionTracker::handleAssertViolation` and the current
429 /// review handler is
430 /// `FuzzTestPreconditionTracker::handleAssertViolation`.
432
433 // CLASS METHODS
434
435 /// Return the current fuzz test handler guard.
436 ///
437 /// \pre The behavior is undefined unless a fuzz test handler guard is currently in scope.
439
440 // ACCESSORS
441
442 /// Return the original assertion handler.
444
445 /// Return the original review handler.
447};
448
449// ============================================================================
450// INLINE DEFINITIONS
451// ============================================================================
452
453inline
478
479inline
495
496inline
498{
499 BSLS_ASSERT(s_currentFuzzTestHandlerGuard_p);
500 return s_currentFuzzTestHandlerGuard_p;
501}
502
503inline
508
509inline
514} // close package namespace
515
516
517#endif
518
519// ----------------------------------------------------------------------------
520// Copyright 2021 Bloomberg Finance L.P.
521//
522// Licensed under the Apache License, Version 2.0 (the "License");
523// you may not use this file except in compliance with the License.
524// You may obtain a copy of the License at
525//
526// http://www.apache.org/licenses/LICENSE-2.0
527//
528// Unless required by applicable law or agreed to in writing, software
529// distributed under the License is distributed on an "AS IS" BASIS,
530// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
531// See the License for the specific language governing permissions and
532// limitations under the License.
533// ----------------------------- END-OF-FILE ----------------------------------
534
535/** @} */
536/** @} */
537/** @} */
Definition bsls_assert.h:2139
static Assert::ViolationHandler violationHandler()
static void setViolationHandler(Assert::ViolationHandler function)
void(* ViolationHandler)(const AssertViolation &)
Definition bsls_assert.h:2220
Definition bsls_fuzztest.h:395
Review::ViolationHandler getOriginalReviewHandler()
Return the original review handler.
Definition bsls_fuzztest.h:510
FuzzTestHandlerGuard()
Definition bsls_fuzztest.h:454
~FuzzTestHandlerGuard()
Definition bsls_fuzztest.h:480
static FuzzTestHandlerGuard * instance()
Definition bsls_fuzztest.h:497
Assert::ViolationHandler getOriginalAssertionHandler()
Return the original assertion handler.
Definition bsls_fuzztest.h:504
Definition bsls_fuzztestpreconditionexception.h:115
static void installHandlers(PreconditionHandlerType beginHandler, PreconditionHandlerType endHandler)
static void noOpHandler()
Do nothing.
Definition bsls_review.h:1097
static Review::ViolationHandler violationHandler()
static void setViolationHandler(Review::ViolationHandler function)
void(* ViolationHandler)(const ReviewViolation &)
Definition bsls_review.h:1196
#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 bdlt_iso8601util.h:707
Definition bsls_fuzztest.h:329
static void handleException(const FuzzTestPreconditionException &exception)
static void initStaticState(const char *fileName)
static void handlePreconditionsBegin()
Increment the assertion/review block depth level counter.
static void handleReviewViolation(const ReviewViolation &violation)
static void handleAssertViolation(const AssertViolation &violation)