// // fuzzing_directed.h // // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. // // The directed fuzzing definitions header. Requires C++0x11 support // due to usage of variadic templates. // #ifndef __FUZZING_DIRECTED_H__ #define __FUZZING_DIRECTED_H__ #pragma once #include #pragma warning(push) #pragma warning(disable : 4242) // Standard random library contains C4242 warning about conversion from unsigned int to unsigned short. #include #pragma warning(pop) #include #include #include #include #pragma warning(disable : 4505) #include "memallocator.h" #ifndef __FUZZING_ALLOCATOR #define __FUZZING_ALLOCATOR fuzz::CFuzzCRTAllocator #endif namespace variadic { template struct index { }; template struct gen_seq : gen_seq { }; template struct gen_seq<0, Is...> : index { }; } namespace fuzz { // Fuzz traits change inherent behavior of the CFuzzType class // (and associated derived classes). This is a bit-flag such that // multiple traits can be applied as needed. enum _FuzzTraits : unsigned int { // Default behavior is to not throw exceptions TRAIT_DEFAULT = 0x0, // In the event that the fuzz map percentages add up to more than // 100% during constructor initialization, an exception of type // CFuzzRangeException is thrown. TRAIT_THROW_ON_INIT_FAILURE = 0x1, // For classes that could realloc a buffer in order to grow or // shrink the size of the fuzzed result, this results in having // two different buffers that need to be freed. To make the calling // code work correctly, we can use the flag below to transfer the // allocation ownership such that the calling code frees the correct // buffer and our fuzz classes frees the other buffer. // Example: // DWORD cbSize = 0; // __untrusted_array_size(BYTE, DWORD) cbSizeUntrusted = // __untrusted_array_size_init(BYTE, DWORD)(cbSize); // __untrusted_array(BYTE, DWORD) arr = // __untrusted_array_init(FUZZ_MAP(...), nullptr, cbSizeUntrusted); // AllocateArray(&arr, &cbSizeUntrusted); // arr overloads & operator to // // allow allocation and then wraps // // this allocation with CFuzzArray // ...use arr, potentially resulting in a reallocated fuzzed buffer... // CoTaskMemFree(arr); // If reallocation occurs, the other buffer // // will be freed when arr goes out of scope. TRAIT_TRANSFER_ALLOCATION = 0x2, // Given the design of the CFuzzArray class, it could be unclear // if the corresponding size of the array is the number of elements // or the overall byte count. The class defaults to assuming // the number of elements, but the trait below can be used to // inform the class that the size is actually the number of total // bytes. Note that when using byte arrays, there is no difference // in behavior. TRAIT_SIZE_IS_BCOUNT = 0x4 }; typedef UINT FuzzTraits; // Percentages used for fuzzing are converted into a range of acceptable // values between 0 and 100. This allows a random value to be generated // between 0 and 100, if it falls within the range then the fuzzing // manipulation will be applied accordingly. // For example: Generate a random value between 0-100 (inclusive) // _Range_ | _Manipulation_ // 91-100 | Fuzz permutation A // 86-90 | Fuzz permutation B // 80-85 | Fuzz permutation C // 0-79 | Default value struct _range { int iHigh; int iLow; }; // Fuzz type entries provide a fuzzing function, specified as pfnFuzz, // together with a percentage that this fuzzing function should be // invoked. The percentage, uiPercentage, should be between 1-100. // There are some fuzzing classes (i.e. CFuzzString) that allow // for fuzzed values to be reallocated, in these cases a function // must be specified via pfnDealloc that can free the resulting // memory appropriately. If no memory is being reallocated, pfnDealloc // can be nullptr. // // An array of fuzz type entries (or fuzz array entries) is referred // to as a fuzz map in subsequent documentation. // // The design of pfnFuzz is to allow for mutational based fuzzing // scenarios, where "template" data is passed to the fuzzing routine // via the function parameter. This data will be whatever the // fuzzing class' value is, either from initialization of the class // or by setting the value directly. // For example: // __untrusted_lpwstr pwsz = // __untrusted_lpwstr_init(FUZZ_MAP(...), L"foo"); // LPWSTR pwszFuzzed = pwsz; // Asking for the LPWSTR member will // // cause the fuzzing map to be // // evaluated, if a fuzz type entry is // // selected, L"foo" will be passed // // as the parameter to pfnFuzz template struct _fuzz_type_entry { unsigned int uiPercentage; std::function<_Type(_Type, _Args...)> pfnFuzz; std::function pfnDealloc; }; // The range structure is an internal structure that maps the percentage // specified within the fuzz type entry struct into its associated // probability range. It is not expected that users of this codebase // will need to use this struct type directly. template struct _range_fuzz_type_entry { _fuzz_type_entry<_Type, _Args...> fte; _range range; }; // Similar to fuzz type entries, there are different challenges when // fuzzing an array, since the size of the array needs to be known // and if a reallocation occurs, the size needs to be updated to stay // in sync. To support this, fuzz array entries have two template // parameters, the first one is the type of objects in the array, // and the second parameter is the size, either in bytes or in element // count. When designing a fuzz map for an array, you will need to // determine whether _Type2 is the byte count or the element count. // // With respect to pfnFuzz, once again this follows a mutational // based approach, where the parameters will be whatever CFuzzArray // is initialized to. Notice that the second parameter is an // automatic reference to the size, which allows this value to be // updated in the event that a new fuzzed buffer is allocation and // returned from pfnFuzz. If reallocation occurs, pfnDealloc must // be provided as a means of appropriately freeing the memory. // For example: // DWORD cbSize = 0; // __untrusted_array_size(BYTE, DWORD) cbSizeUntrusted = // __untrusted_array_size_init(cbSize); // __untrusted_array(BYTE, DWORD) arr = // __untrusted_array_init(FUZZ_MAP(...), nullptr, cbSizeUntrusted); // AllocateArray(&arr, &cbSizeUntrusted); // Operator overloading on & will // // allow the array to be allocated // // and then wrapped by CFuzzArray // BYTE *rgFuzzed = arr; // Asking for the BYTE* value will cause // // the fuzz map to be evaluated. If the // // buffer is reallocated, pfnFuzz will // // have a reference to cbSize which can be // // updated appropriately. template struct _fuzz_array_entry { unsigned int uiPercentage; std::function<_Type1*(_Type1*, _Type2&, _Args...)> pfnFuzz; std::function pfnDealloc; }; // Internal struct for mapping percentages to value ranges. Not // expected to be used by users of this codebase but for internal // use instead. template struct _range_fuzz_array_entry { _fuzz_array_entry<_Type1, _Type2, _Args...> fae; _range range; }; // During initialization of the fuzz classes, if the fuzz map // percentages add up to more than 100% and the fuzz trait // TRAIT_THROW_ON_INIT_FAILURE is applied, the CFuzzRangeException // will be thrown. Since constructors cannot return errors, this allows // for verifying that numeric errors have not been made when designing // the fuzz map that would cause fuzzing manipulations to not ever // be applied. Not that this exception cannot ever be thrown from // CFuzzFlags, as the fuzz map is evaluated slightly differently when // fuzzing bit-wise flags. class CFuzzRangeException { public: CFuzzRangeException(){}; virtual ~CFuzzRangeException(){}; }; // In an effort to avoid fuzzing code from scattering rand() throughout // the codebase, the CFuzzChance class is designed as the go to place // for generating random values (or random selection of values). This // class is also used by the various fuzz classes internally for // determining which fuzzing routine should be applied from the fuzz map. class CFuzzChance { public: // Will collide with min() and max() macros, so we need to jump through hoops // to correctly use std::numeric_limits<_Type>::min() and std::numeric_limits<_Type>::max() #ifdef min #define __min_collision__ #undef min #endif #ifdef max #define __max_collision__ #undef max #endif template static _Type GetRandom() throw() { return GetRandom<_Type>(std::numeric_limits<_Type>::min(), std::numeric_limits<_Type>::max()); } template static _Type GetRandom(__in _Type tCap) throw() { return GetRandom<_Type>(std::numeric_limits<_Type>::min(), --tCap); } template static _Type GetRandom(__in _Type tMin, __in _Type tMax) { std::mt19937 engine(m_rd()); // Mersenne twister MT19937 std::uniform_int_distribution<_Type> distribution(tMin, tMax); auto generator = std::bind(distribution, engine); return generator(); } // uniform_int_distribution only works with _Is_IntType types, which do not // currently include char or unsigned char, so here is a specialization // specifically for BYTE (unsigned char). template<> static BYTE GetRandom(__in BYTE tMin, __in BYTE tMax) { std::mt19937 engine(m_rd()); // Mersenne twister MT19937 // BYTE is unsigned, so we want to also use an unsigned type to avoid sign // extension of tMin and tMax. std::uniform_int_distribution distribution(tMin, tMax); auto generator = std::bind(distribution, engine); return static_cast(generator()); } #ifdef __min_collision__ #undef __min_collision__ #define min(a, b) (((a) < (b)) ? (a) : (b)) #endif #ifdef __max_collision__ #undef __max_collision__ #define max(a, b) (((a) > (b)) ? (a) : (b)) #endif // Given an array of elements, select a random element from the // collection. Note that cElems is the number of items in the array. template static _Type SelectOne(__in_ecount(cElems) const _Type* rg, __in size_t cElems) throw() { return rg[GetRandom(cElems)]; } // Given an array of elements, select a random element from the // collection. Note that _cElems is the number of items in the array. template static _Type SelectOne(const _Type (&rg)[_cElems]) throw() { return rg[GetRandom(_cElems)]; } private: CFuzzChance() {} virtual ~CFuzzChance() {} static std::random_device m_rd; }; std::random_device CFuzzChance::m_rd; // Provides a common base class between CFuzzArray and CFuzzType, // collecting the set of members that are used by both classes. This // class cannot be instantiated directly and must be inherited from. class CFuzzBase { protected: CFuzzBase() : m_fFuzzed(FALSE), m_iPercentageTotal(100){}; virtual ~CFuzzBase(){}; // Converts a percentage into a valid range. Note that riTotal // is a reference value, which allows for a running total to be // decremented as fuzz map percentages are mapped to valid ranges. void ConvertPercentageToRange(__in unsigned int iPercentage, __inout int& riTotal, __deref_out _range* pr) const { pr->iHigh = riTotal; pr->iLow = riTotal - iPercentage; riTotal -= iPercentage; } protected: BOOL m_fFuzzed; int m_iPercentageTotal; FuzzTraits m_traits{ TRAIT_DEFAULT }; }; // The CFuzzArray class is designed to allow fuzzing of element // arrays, potentially reallocating a fuzzed version of the array // that is either larger or smaller than the template buffer. Whether // reallocation is possible (or an appropriate fuzzing strategy) is // dependent on the scenario and must be determined by the person // designing the fuzz map. If reallocation is going to be used, the // _Alloc class must specify the appropriate allocator that corresponds // to the code that is being fuzzed. For example, if all allocations // are made using new/delete, the default CComAllocator is not // appropriate and should be changed to CCRTAllocator. Adding new // allocator classes is as easy as writing a new class that supports // Allocate/Free/Reallocate, see documentation for CComAllocator. template class CFuzzArray : public CFuzzBase { public: template friend class CFuzzArraySize; // Creates a CFuzzArray instance that wraps a buffer specified by // rg, together with its size (note that this is the number of elements // not necessarily the byte count). cElems is a reference so it must // point to a valid variable. In this constructor, it is valid for // the fuzz map to be a null pointer, but it is expected that a fuzz // map will be provided at a later time via SetFuzzArrayMap or // AddFuzzArrayEntry. // // If CFuzzArray is initialized with rg == nullptr, this usage case // is designed to leverage the & operator overload to have CFuzzArray // initialized to a valid buffer. In this scenario, if reallocation // is being used by the fuzz map and reallocation occurs, this class // implements logic to transfer the fuzzed allocation to the calling code // and then free the array that was set via the & operator. If this is // not desirable, the situation is avoided by either not reallocating // or by not initializing via &. CFuzzArray( __in_ecount(cfae) const _fuzz_array_entry<_Type1, _Type2, _Args...>* rgfae, __in ULONG cfae, __in_ecount_opt(cElems) _Type1* rg, __inout _Type2& cElems, __in _Args&&... args) : m_rgCaller(rg), m_pcElems(&cElems), m_pfas(nullptr), m_tArgs(std::forward<_Args>(args)...) { Init(rgfae, cfae); } // Constructor that allows association with a companion CFuzzArraySize object. // See CFuzzArraySize comments for more details. CFuzzArray( __in_ecount(cfae) const _fuzz_array_entry<_Type1, _Type2, _Args...>* rgfae, __in ULONG cfae, __in_opt _Type1* rg, __in CFuzzArraySize<_Type1, _Type2, _Args...>& size, __in _Args&&... args) : m_rgCaller(rg), m_pcElems(size.m_pcElems), m_pfas(nullptr), m_tArgs(std::forward<_Args>(args)...) { if (SUCCEEDED(Init(rgfae, cfae))) { size.Pair(*this); m_pfas = size.Reference(); } } virtual ~CFuzzArray() { FreeRealloc(); } // Requesting the array pointer will result in the fuzz map being // evaluated, potentially returning a fuzzed value. Note that fuzzing // is only evaluated once, so repeated access will return the same // fuzzed choice. __inline operator _Type1*() throw() { return GetValueFromMap(); } // Allow calls to get the size of the fuzzed array ensuring evaluation // of the fuzzmap. Designed primarily to be called from the CFuzzArraySize // friend class. __inline operator _Type2() throw() { GetValueFromMap(); return *m_pcElems; } // The overloaded & operator allows this class to replace array pointers // within the calling code, but the associated size need to be initialized // via the constructor. If this operator is used and the fuzzing map // applies reallocation, ownership of the respective buffers is transferred // such that the fuzzed buffer becomes the caller's responsibility and the // initial buffer is freed when this class is destroyed. Users of this // codebase are responsible for ensuring the correct allocator is specified // for _Alloc and that the calling code is still functionally correct. __inline _Type1** operator&() throw() { assert(m_rgCaller == nullptr); m_ftEffectiveTraits |= TRAIT_TRANSFER_ALLOCATION; return (m_rgRealloc) ? &m_rgRealloc : &m_rgCaller; } // Const version of this operator overload does not assume ownership transfer // like the above case. __inline const _Type1** operator&() const throw() { assert(m_rgCaller == nullptr); return (m_rgRealloc) ? &m_rgRealloc : &m_rgCaller; } // Setting the fuzz map will clear any previous applied fuzz map. The // AddFuzzArrayEntry function can be used to add additional fuzz map // entries without removing the existing map. Returns E_INVALIDARG in the // event that the total percentages add up to more than 100%. [[nodiscard]] __inline HRESULT SetFuzzArrayMap( __in_ecount(cfae) const _fuzz_array_entry<_Type1, _Type2, _Args...>* rgfae, __in ULONG cfae) throw() { ClearFuzzArrayEntries(); for (ULONG i = 0; i < cfae; i++) { AddFuzzArrayEntry(rgfae[i].uiPercentage, rgfae[i].pfnFuzz, rgfae[i].pfnDealloc); } return (m_iPercentageTotal >= 0) ? S_OK : E_INVALIDARG; } // Adds an additional fuzz map entry, without clearing the existing // fuzz map. Returns E_INVALIDARG in the event that the total percentages // add up to more than 100%. [[nodiscard]] HRESULT AddFuzzArrayEntry( __in unsigned int uiPercentage, __in std::function<_Type1*(_Type1*, _Type2&, _Args...)> pfnFuzz, __in std::function pfnDealloc = nullptr) throw() { _range_fuzz_array_entry<_Type1, _Type2, _Args...> r = { 0 }; r.fae.uiPercentage = uiPercentage; r.fae.pfnFuzz = pfnFuzz; r.fae.pfnDealloc = pfnDealloc; ConvertPercentageToRange(uiPercentage, m_iPercentageTotal, &r.range); m_map.push_back(r); return (m_iPercentageTotal >= 0) ? S_OK : E_INVALIDARG; } void ClearFuzzArrayEntries() throw() { m_map.clear(); m_iPercentageTotal = 100; } // Invokes the fuzz map in the event that fuzzing as not been applied. // Since the fuzz map entries have their own potential allocation and // deallocation routines, we actually make another copy of the fuzzed // buffer in the event that reallocation has occurred (determined by // comparing against the original initialized pointer value). This // is important because we use the _Alloc value to ensure the correct // memory allocator is used that is appropriate for the calling code. __inline _Type1* GetValueFromMap() { if (!m_fFuzzed) { m_fFuzzed = TRUE; WORD wRandom = CFuzzChance::GetRandom(100); for (auto& r : m_map) { if (r.range.iLow <= wRandom && wRandom < r.range.iHigh) { _Type1* rgTemp = CallFuzzMapFunction(r.fae.pfnFuzz, m_rgCaller, *m_pcElems, m_tArgs); if (rgTemp && rgTemp != m_rgCaller) { size_t cbRealloc = (m_ftEffectiveTraits & TRAIT_SIZE_IS_BCOUNT) ? *m_pcElems : *m_pcElems * sizeof(_Type1); m_rgRealloc = reinterpret_cast<_Type1*>(_Alloc::Allocate(cbRealloc)); if (m_rgRealloc) { memcpy_s(m_rgRealloc, cbRealloc, rgTemp, cbRealloc); } if (r.fae.pfnDealloc) { r.fae.pfnDealloc(rgTemp); } } break; } } } return (m_rgRealloc) ? m_rgRealloc : m_rgCaller; } private: _Type1* m_rgCaller; _Type1* m_rgRealloc; _Type2* m_pcElems; FuzzTraits m_ftEffectiveTraits; CFuzzArraySize<_Type1, _Type2, _Args...>* m_pfas; std::vector<_range_fuzz_array_entry<_Type1, _Type2, _Args...>> m_map; std::tuple<_Args...> m_tArgs; CFuzzArray<__FUZZING_ALLOCATOR, _Type1, _Type2, _Args...>* Reference() { return this; } template _Type1* CallFuzzMapFunction( std::function<_Type1*(_Type1*, _Type2&, _Args...)> pfnFuzz, _Type1* t1, _Type2& t2, std::tuple<_Args...>& tup, variadic::index) { return pfnFuzz(t1, t2, std::get(tup)...); } _Type1* CallFuzzMapFunction( std::function<_Type1*(_Type1*, _Type2&, _Args...)> pfnFuzz, _Type1* t1, _Type2& t2, std::tuple<_Args...>& tup) { return CallFuzzMapFunction(pfnFuzz, t1, t2, tup, variadic::gen_seq{}); } [[nodiscard]] HRESULT Init( __in_ecount(cfae) const _fuzz_array_entry<_Type1, _Type2, _Args...>* rgfae, __in ULONG cfae) { m_rgRealloc = nullptr; m_ftEffectiveTraits = m_traits; // Since constructors cannot return error values, the // TRAIT_THROW_ON_INIT_FAILURE trait allows for an exception // to be thrown in the event that this class was not initialized // correctly. The intended purpose is to catch users of this // codebase who have incorrectly specified fuzz maps that add up // to more than 100%. HRESULT hr = SetFuzzArrayMap(rgfae, cfae); if (FAILED(hr) && (m_traits & TRAIT_THROW_ON_INIT_FAILURE)) { throw CFuzzRangeException(); } if (m_rgCaller == nullptr) { m_ftEffectiveTraits |= TRAIT_TRANSFER_ALLOCATION; } return hr; } void FreeRealloc() { if (m_rgRealloc) { // See comments about explaining when we transfer // allocation and deallocation responsibilities. if (m_ftEffectiveTraits & TRAIT_TRANSFER_ALLOCATION) { _Alloc::Free(m_rgCaller); m_rgCaller = nullptr; } else { _Alloc::Free(m_rgRealloc); m_rgRealloc = nullptr; } } } }; // When working with arrays, care must be taken when passing a pointer to a // fuzzed array together with the size to ensure the size lines up with the // the evaluated fuzz map. Consider the following: // // BYTE rg[10] = {0}; // size_t cb = ARRAYSIZE(rg); // CFuzzArray rgUntrusted(FUZZ_MAP(...), rg, cb); // hr = foo(rgUntrusted, cb); // // When evaluating the arguments for the function foo, the value of cb // will be pushed as an argument before fa has a chance to evaluate the fuzzing map. // This results in the cb parameter to foo being the original value, even if the // fuzzing map alters cb. // // To account for this CFuzzArraySize pairs together with a CFuzzArray instance to ensure // the fuzz map is evaluated prior to either the array pointer or the size being used. // For example: // // BYTE rg[10] = {0}; // size_t cb = ARRAYSIZE(rg); // __untrusted_array_size(BYTE, size_t) cbUntrusted = __untrusted_array_size_init(cb); // __untrusted_array(BYTE, size_t) rgUntrusted = __untrusted_array_init(FUZZ_MAP(...), rg, cbUntrusted); // hr = foo(rgUntrusted, cbUntrusted); template class CFuzzArraySize { public: template friend class CFuzzArray; CFuzzArraySize(__inout _Type2& cElems) : m_pcElems(&cElems), m_pfa(nullptr) { } virtual ~CFuzzArraySize() {} __inline operator _Type2() throw() { if (m_pfa) { m_pfa->GetValueFromMap(); } return *m_pcElems; } __inline _Type2* operator&() throw() { return m_pcElems; } __inline const _Type2* operator&() const throw() { return m_pcElems; } __inline void Pair(__in CFuzzArray<__FUZZING_ALLOCATOR, _Type1, _Type2, _Args...>& rfa) { m_pfa = rfa.Reference(); } private: CFuzzArray<__FUZZING_ALLOCATOR, _Type1, _Type2, _Args...>* m_pfa; _Type2* m_pcElems; CFuzzArraySize<_Type1, _Type2, _Args...>* Reference() { return this; } }; // The CFuzzType class is primarily designed for primitive types but // is also used as the base class for CFuzzTypePtr and CFuzzLpwstr. // The various operator overloads allow this class to wrap a type and // usage of that type works transparently. Asking for the value // of the type wrapped by this class will invoke the fuzzing map. template class CFuzzType : public CFuzzBase { public: // Creates an instance of CFuzzType, initializing the value to // the value specified in t. Note that providing a fuzz map // is optional, but then expected to be provided via SetFuzzTypeMap // or AddFuzzTypeEntry if fuzzing is to be applied. CFuzzType( __in_ecount(cfte) const _fuzz_type_entry<_Type, _Args...>* rgfte, __in ULONG cfte, __in _Type t, __in _Args&&... args) : m_t(t), m_tInit(t), m_tArgs(std::forward<_Args>(args)...) { m_pfnOnFuzzedValueFromMap = [](_Type t, std::function) { return t; }; HRESULT hr = SetFuzzTypeMap(rgfte, cfte); // Since constructors cannot return error values, the // TRAIT_THROW_ON_INIT_FAILURE trait allows for an exception // to be thrown in the event that this class was not initialized // correctly. The intended purpose is to catch users of this // codebase who have incorrectly specified fuzz maps that add up // to more than 100%. if (FAILED(hr) && (m_traits & TRAIT_THROW_ON_INIT_FAILURE)) { throw CFuzzRangeException(); } } virtual ~CFuzzType() { } // Initializes with the value specified by t and then applies // the fuzz map to produce a fuzzed return value. Usage of this // operator will make the CFuzzType instance look like a function // call, which might be desirable in some instances. // For example: // __untrusted_t(int) iVal = __untrusted_init(FUZZ_MAP(...), 10); // int iFuzzedVal = iVal(15); // re-initializes the default value to 15, // // then applies the fuzz map to return // // a potentially fuzzed value __inline _Type operator()(__in _Type t) throw() { m_t = m_tInit = t; return GetValueFromMap(); } // Will reinitialize the value for the internally wrapped type. // If the fuzzing map has already been evaluated, initializing // via the = operator will not cause the fuzzing map to be // evaluated when the value is subsequently retrieved. __inline void operator=(__in _Type t) throw() { m_t = m_tInit = t; } // Getting the value of this instance will cause the fuzz map // to be evaluated. The fuzz map will only be evaluated once, // subsequent access will always return the selected value. __inline operator _Type() throw() { return GetValueFromMap(); } // Allows initialization of the internal type by providing a // reference. If the fuzz map has been evaluated, the reference // is provided to the fuzzed value. __inline virtual _Type* operator&() throw() { return (m_fFuzzed) ? &m_t : &m_tInit; } // Similar to above, if the fuzz map has been evaluated, the // reference is provided to the fuzzed value. __inline const _Type* operator&() const throw() { return (m_fFuzzed) ? &m_t : &m_tInit; } // Setting the fuzz map will clear any previous applied fuzz map. The // AddFuzzArrayEntry function can be used to add additional fuzz map // entries without removing the existing map. Returns E_INVALIDARG in the // event that the total percentages add up to more than 100%. [[nodiscard]] __inline HRESULT SetFuzzTypeMap( __in_ecount(cfte) const _fuzz_type_entry<_Type, _Args...>* rgfte, __in ULONG cfte) throw() { ClearFuzzTypeEntries(); bool fInvalidEntry{}; for (ULONG i{}; i < cfte; ++i) { // Process all entries; failure will be returned at the end. fInvalidEntry |= FAILED(AddFuzzTypeEntry(rgfte[i].uiPercentage, rgfte[i].pfnFuzz, rgfte[i].pfnDealloc)); } return (fInvalidEntry || (m_iPercentageTotal >= 0)) ? S_OK : E_INVALIDARG; } // Adds an additional fuzz map entry, without clearing the existing // fuzz map. Returns E_INVALIDARG in the event that the total percentages // add up to more than 100%. [[nodiscard]] HRESULT AddFuzzTypeEntry( __in unsigned int uiPercentage, __in std::function<_Type(_Type, _Args...)> pfnFuzz, __in std::function pfnDealloc = nullptr) throw() { _range_fuzz_type_entry<_Type, _Args...> r = { 0 }; r.fte.uiPercentage = uiPercentage; r.fte.pfnFuzz = pfnFuzz; r.fte.pfnDealloc = pfnDealloc; ConvertPercentageToRange(uiPercentage, m_iPercentageTotal, &r.range); m_map.push_back(r); return (m_iPercentageTotal >= 0) ? S_OK : E_INVALIDARG; } void ClearFuzzTypeEntries() throw() { m_map.clear(); m_iPercentageTotal = 100; } protected: _Type m_t; _Type m_tInit; std::vector<_range_fuzz_type_entry<_Type, _Args...>> m_map; std::function<_Type(_Type, std::function)> m_pfnOnFuzzedValueFromMap; std::tuple<_Args...> m_tArgs; _Type CallFuzzMapFunction( std::function<_Type(_Type, _Args...)> pfnFuzz, _Type t, std::tuple<_Args...>& tup) { return CallFuzzMapFunction(pfnFuzz, t, tup, variadic::gen_seq{}); } // To support sub classes ability to realloc fuzzed values, // the m_pfnOnFuzzedValueFromMap will be invoked whenever a fuzz map // entry is selected. When not overridden, the default behavior is to // set this classes value to the fuzzed value returned from the // associated fuzzing routine. For an example that alters this behavior // take a look at the CFuzzLpwstr class. __inline virtual _Type GetValueFromMap() { if (!m_fFuzzed) { m_fFuzzed = TRUE; m_t = m_tInit; WORD wRandom = CFuzzChance::GetRandom(100); for (auto& r : m_map) { if (r.range.iLow <= wRandom && wRandom < r.range.iHigh) { m_t = m_pfnOnFuzzedValueFromMap(CallFuzzMapFunction(r.fte.pfnFuzz, m_tInit, m_tArgs), r.fte.pfnDealloc); break; } } } return m_t; } private: template _Type CallFuzzMapFunction( std::function<_Type(_Type, _Args...)> pfnFuzz, _Type t, std::tuple<_Args...>& tup, variadic::index) { UNREFERENCED_PARAMETER(tup); // Compiler gets confused by the expansion of tup below. return pfnFuzz(t, std::get(tup)...); } }; // The CFuzzTypePtr class provides all the same functionality as the // base CFuzzType class, but also provides an additional operator // override for ->. This allows pointers to be wrapped by this class // to be seamlessly used in the calling codebase. Note that correct // fuzzing behavior is entirely dependent on the design of the fuzzing // map, this class is not intended to fuzz actual pointer values. template class CFuzzTypePtr : public CFuzzType<_Type, _Args...> { public: // Note that _Type is expected to be a pointer type (thus making // the operator override of -> make sense). It is the callers // responsibility to set the type appropriately. CFuzzTypePtr( __in_ecount(cfte) const _fuzz_type_entry<_Type>* rgfte, __in ULONG cfte, __in _Type pt, __in _Args&&... args) : CFuzzType(rgfte, cfte, pt, std::forward<_Args>(args)...) { } virtual ~CFuzzTypePtr() {} _Type operator->() const throw() { return (this->m_fFuzzed) ? this->m_t : CFuzzType<_Type, _Args...>::m_tInit; } // This operator makes it possible to invoke the fuzzing map // by calling this class like a parameterless function. This is // used to support the __make_untrusted call below. Note that once // the fuzzing map is invoked, all future access of the pointer // members will be pointed to potentially fuzzed data (as per // the logic of the fuzz map). __inline void operator()() throw() { CFuzzType<_Type, _Args...>::GetValueFromMap(); } }; // The CFuzzLpwstr class extends the CFuzzType class in order to // provide the ability to realloc fuzzed strings. This is important // because it might be desirable to grow or shrink a fuzzed string // based upon the scenario. Because reallocation can occur, this // class needs to maintain similar logic to the CFuzzArray class for // transferring ownership of buffers. Similarly, if reallocation // is allowed, _Alloc needs to match the appropriate allocation // method used by the calling code. Please see CFuzzArray documentation // for more details. template class CFuzzString : public CFuzzType<_Type*, _Args...> { public: CFuzzString( __in_ecount(cfte) const _fuzz_type_entry<_Type*, _Args...>* rgfte, __in ULONG cfte, __in _Type* psz, __in _Args... args) : CFuzzType(rgfte, cfte, psz, std::forward<_Args>(args)...) { OnFuzzedValueFromMap(); } virtual ~CFuzzString() { FreeFuzzedString(); } // Provide operator overloading to allow this class to be // initialized across function calls that would allocate // a string. It is assumed that if initialization occurs // via this method and then reallocation occurs, it will // be necessary to transfer ownership of who frees the // appropriate buffer. // For example: // __untrusted_lpwstr pwsz = // __untrusted_lpwstr_init(FUZZ_MAP(...), nullptr); // AllocateString(&pwsz); // ...use pwsz to invoke fuzzing map... // CoTaskMemFree(pwsz); // If reallocation occurs, // // using pwsz will provide the // // fuzzed string. This means // // pwsz is responsible for freeing // // the original allocation. __inline virtual _Type** operator&() throw() { m_ftEffectiveTraits |= TRAIT_TRANSFER_ALLOCATION; return (this->m_fFuzzed) ? &(this->m_t) : &CFuzzType<_Type, _Args...>::m_tInit; } private: _Type* m_pszFuzzed; FuzzTraits m_ftEffectiveTraits; // Since reallocation could occur that is dependent on matching the // allocation routines of the calling code, we want to ensure we // allocate using the correct allocator specified by _Alloc. However, // the allocation routines in the fuzzing map could differ, therefore // we copy the fuzzed string as appropriate and use the specified // dealloc function in the fuzz map entry to delete the fuzzed string // created by the fuzz map entry. Slightly inefficient, but safer // and supports a wider variety of scenarios. void OnFuzzedValueFromMap() { m_pszFuzzed = nullptr; m_ftEffectiveTraits = this->m_traits; this->m_pfnOnFuzzedValueFromMap = [&](_Type* psz, std::function dealloc) { FreeFuzzedString(); _Type* pszFuzzed = psz; if (psz && psz != this->m_tInit) { size_t cb = (sizeof(_Type) == sizeof(char)) ? (strlen(reinterpret_cast(psz)) + 1) * sizeof(char) : (wcslen(reinterpret_cast(psz)) + 1) * sizeof(WCHAR); m_pszFuzzed = reinterpret_cast<_Type*>(_Alloc::Allocate(cb)); if (m_pszFuzzed) { (sizeof(_Type) == sizeof(char)) ? StringCbCopyA(reinterpret_cast(m_pszFuzzed), cb, reinterpret_cast(psz)) : StringCbCopyW(reinterpret_cast(m_pszFuzzed), cb, reinterpret_cast(psz)); pszFuzzed = m_pszFuzzed; } if (dealloc) { dealloc(psz); } } return pszFuzzed; }; } void FreeFuzzedString() { if (m_pszFuzzed) { // See comments about explaining when we transfer // allocation and deallocation responsibilities. if (m_ftEffectiveTraits & TRAIT_TRANSFER_ALLOCATION) { _Alloc::Free(this->m_tInit); this->m_tInit = nullptr; } else { _Alloc::Free(m_pszFuzzed); m_pszFuzzed = nullptr; } } } }; // The CFuzzFlags class extends the CFuzzType base class, but // operates slightly differently than other fuzz class implementations. // The intended usage of this class is when dealing with bit flags, // where covering the range of possible bit flag combinations would be // very tedious for the fuzz map designer. To aid in this scenario, // the fuzz map entries are evaluated on a per flag basis, meaning the // chance of inserting that flag is dependent on the percentage value. // For example: // In the below fuzz map, CFuzzFlags will interpret this to mean // apply STARTF_FORCEONFEEDBACK 10% of the time, together with // STARTF_FORCEOFFFEEDBACK 2% of the time, together with // STARTF_PREVENTPINNING %1 of the time, etc. // // _fuzz_type_entry _fuzz_dwFlags_map[] = // { // { 10, [] (DWORD) { return STARTF_FORCEONFEEDBACK; } }, // { 2, [] (DWORD) { return STARTF_FORCEOFFFEEDBACK; } }, // { 1, [] (DWORD) { return STARTF_PREVENTPINNING; } }, // { 50, [] (DWORD) { return STARTF_RUNFULLSCREEN; } }, // }; // // Also note that this class should specifically not have the // TRAIT_THROW_ON_INIT_FAILURE trait, as it is expected that the // percentages will total more than 100% when added together. template class CFuzzFlags : public CFuzzType<_Type, _Args...> { public: CFuzzFlags( __in_ecount(cfte) const _fuzz_type_entry<_Type>* rgfte, __in ULONG cfte, __in _Type flags, __in _Args&&... args) : CFuzzType(rgfte, cfte, flags, std::forward<_Args>(args)...) { } virtual ~CFuzzFlags() { } protected: __inline virtual _Type GetValueFromMap() { if (!this->m_fFuzzed) { this->m_t = 0; this->m_fFuzzed = TRUE; for (auto& r : this->m_map) { // Generate a new random value during each map entry // and use it to evaluate if each individual fuzz map // entry should be applied. WORD wRandom = CFuzzChance::GetRandom(100); // Translate percentages to allow for each flag to be considered // for inclusion independently. int iHigh = 100; int iLow = iHigh - (r.range.iHigh - r.range.iLow); if (iLow <= wRandom && wRandom < iHigh) { this->m_t |= CallFuzzMapFunction(r.fte.pfnFuzz, this->m_tInit, CFuzzType<_Type, _Args...>::m_tArgs); } } } return this->m_t; } }; } // To make using these classes a no-opt if fuzzing is not intended to // be applied, the following set of functions and defines operate // differently if __GENERATE_DIRECTED_FUZZING is defined. If defined, // these wrappers use the correct fuzz classes and initialize these // classes with the appropriate map specified by the caller. If not // defined, essentially the use of these classes disappear and the // remaining functions are optimized away by the compiler. When // applied appropriately, not turning on fuzzing leaves no performance // impact, no size footprint, and no leakage of information into the // symbols. #ifdef __GENERATE_DIRECTED_FUZZING #define FUZZ_ARRAY_START_STATIC(name, type, size, ...) static const fuzz::_fuzz_array_entry name[] = { #define FUZZ_ARRAY_START(name, type, size, ...) const fuzz::_fuzz_array_entry name[] = { #define FUZZ_ARRAY_END() \ } \ ; #define FUZZ_TYPE_START_STATIC(name, type, ...) static const fuzz::_fuzz_type_entry name[] = { #define FUZZ_TYPE_START(name, type, ...) const fuzz::_fuzz_type_entry name[] = { #define FUZZ_TYPE_END() \ } \ ; #define FUZZ_MAP_ENTRY_ALLOC(i, pfnAlloc, pfnDelete) { i, pfnAlloc, pfnDelete }, #define FUZZ_MAP_ENTRY(i, pfnAlloc) { i, pfnAlloc }, #define FUZZ_MAP(x) x, ARRAYSIZE(x) #define __untrusted_t(type, ...) fuzz::CFuzzType #define __untrusted_lpwstr_t(...) fuzz::CFuzzString<__FUZZING_ALLOCATOR, WCHAR, __VA_ARGS__> #define __untrusted_lpwstr fuzz::CFuzzString<__FUZZING_ALLOCATOR, WCHAR> #define __untrusted_lpstr_t(...) fuzz::CFuzzString<__FUZZING_ALLOCATOR, CHAR, __VA_ARGS__> #define __untrusted_lpstr fuzz::CFuzzString<__FUZZING_ALLOCATOR, CHAR> #define __untrusted_ptr(ptr, ...) fuzz::CFuzzTypePtr #define __untrusted_array(ptr, size, ...) fuzz::CFuzzArray<__FUZZING_ALLOCATOR, ptr, size, __VA_ARGS__> #define __untrusted_array_size(ptr, size, ...) fuzz::CFuzzArraySize template static fuzz::CFuzzType<_Type, _Args...> __untrusted_init( __in_ecount(cfte) const fuzz::_fuzz_type_entry<_Type, _Args...>* rgfte, __in ULONG cfte, __in _Type t, __in _Args&&... args) { return fuzz::CFuzzType<_Type, _Args...>(rgfte, cfte, t, std::forward<_Args>(args)...); } template static fuzz::CFuzzString<__FUZZING_ALLOCATOR, WCHAR, _Args...> __untrusted_lpwstr_init( __in_ecount(cfte) const fuzz::_fuzz_type_entry* rgfte, __in ULONG cfte, __in LPWSTR pwsz, __in _Args&&... args) { return fuzz::CFuzzString<__FUZZING_ALLOCATOR, WCHAR, _Args...>(rgfte, cfte, pwsz, std::forward<_Args>(args)...); } template static fuzz::CFuzzString<__FUZZING_ALLOCATOR, CHAR, _Args...> __untrusted_lpstr_init( __in_ecount(cfte) const fuzz::_fuzz_type_entry* rgfte, __in ULONG cfte, __in LPSTR psz, __in _Args&&... args) { return fuzz::CFuzzString<__FUZZING_ALLOCATOR, CHAR, _Args...>(rgfte, cfte, psz, std::forward<_Args>(args)...); } template static fuzz::CFuzzArray<__FUZZING_ALLOCATOR, _Type1, _Type2, _Args...> __untrusted_array_init( __in_ecount(cfae) const fuzz::_fuzz_array_entry<_Type1, _Type2, _Args...>* rgfae, __in ULONG cfae, __in _Type1* pt, __in fuzz::CFuzzArraySize<_Type1, _Type2, _Args...>& rfas, __in _Args&&... args) { return fuzz::CFuzzArray<__FUZZING_ALLOCATOR, _Type1, _Type2, _Args...>(rgfae, cfae, pt, rfas, std::forward<_Args>(args)...); } template static fuzz::CFuzzArraySize<_Type1, _Type2, _Args...> __untrusted_array_size_init(__inout _Type2& t2) { return fuzz::CFuzzArraySize<_Type1, _Type2, _Args...>(t2); } template static void __make_untrusted_ptr(__in fuzz::CFuzzTypePtr<_Type, _Args...>& rftp) { rftp(); } #else #define FUZZ_ARRAY_START_STATIC(name, type, size, ...) #define FUZZ_ARRAY_START(name, type, size, ...) #define FUZZ_ARRAY_END() #define FUZZ_TYPE_START_STATIC(name, type, ...) #define FUZZ_TYPE_START(name, type, ...) #define FUZZ_TYPE_END() #define FUZZ_MAP_ENTRY_ALLOC(i, pfnAlloc, pfnDelete) #define FUZZ_MAP_ENTRY(i, pfnAlloc) #define FUZZ_MAP(x) nullptr, 0 #define __untrusted_t(type, ...) type #define __untrusted_lpwstr_t(...) LPWSTR #define __untrusted_lpwstr LPWSTR #define __untrusted_lpstr_t(...) LPSTR #define __untrusted_lpstr LPSTR #define __untrusted_ptr(type, ...) type #define __untrusted_array(ptr, size, ...) ptr* #define __untrusted_array_size(ptr, size, ...) size template static __forceinline _Type __untrusted_init( __in_opt void*, __in ULONG, __in _Type t, __in _Args&&...) { return t; } template static __forceinline LPWSTR __untrusted_lpwstr_init( __in_opt void*, __in ULONG, __in LPWSTR pwsz, __in _Args&&...) { return pwsz; } template static __forceinline LPSTR __untrusted_lpstr_init( __in_opt void*, __in ULONG, __in LPSTR psz, __in _Args&&...) { return psz; } template static __forceinline _Type1* __untrusted_array_init( __in_opt void*, __in ULONG, __in _Type1* pt, __in _Type2, __in _Args&&...) { return pt; } template static _Type2 __untrusted_array_size_init(_Type2 t2) { return t2; } template static __forceinline void __make_untrusted_ptr(__in _Type&) { return; } #endif #endif