xref: /aosp_15_r20/external/skia/fuzz/Fuzz.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2016 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef Fuzz_DEFINED
9 #define Fuzz_DEFINED
10 
11 #include "include/core/SkData.h"
12 #include "include/core/SkImageFilter.h"
13 #include "include/core/SkRegion.h"
14 #include "include/core/SkTypes.h"
15 #include "include/private/base/SkMalloc.h"
16 #include "include/private/base/SkTFitsIn.h"
17 #include "tools/Registry.h"
18 
19 #include <limits>
20 #include <cmath>
21 #include <signal.h>
22 #include <limits>
23 
24 class Fuzz {
25 public:
Fuzz(const uint8_t * data,size_t size)26     explicit Fuzz(const uint8_t* data, size_t size) : fData(data), fSize(size), fNextByte(0) {}
27     Fuzz() = delete;
28 
29     // Make noncopyable
30     Fuzz(Fuzz&) = delete;
31     Fuzz& operator=(Fuzz&) = delete;
32 
33     // Returns the total number of "random" bytes available.
size()34     size_t size() const {
35         return fSize;
36     }
37 
38     // Returns if there are no bytes remaining for fuzzing.
exhausted()39     bool exhausted() const {
40         return fSize == fNextByte;
41     }
42 
deplete()43     void deplete() {
44         fNextByte = fSize;
45     }
46 
remainingSize()47     size_t remainingSize() const {
48         return fSize - fNextByte;
49     }
50 
remainingData()51     const uint8_t *remainingData() const {
52         return fData + fNextByte;
53     }
54 
55     // next() loads fuzzed bytes into the variable passed in by pointer.
56     // We use this approach instead of T next() because different compilers
57     // evaluate function parameters in different orders. If fuzz->next()
58     // returned 5 and then 7, foo(fuzz->next(), fuzz->next()) would be
59     // foo(5, 7) when compiled on GCC and foo(7, 5) when compiled on Clang.
60     // By requiring params to be passed in, we avoid the temptation to call
61     // next() in a way that does not consume fuzzed bytes in a single
62     // platform-independent order.
63     template <typename T>
next(T * t)64     void next(T* t) { this->nextBytes(t, sizeof(T)); }
65 
66     // This is a convenient way to initialize more than one argument at a time.
67     template <typename Arg, typename... Args>
68     void next(Arg* first, Args... rest);
69 
70     // nextRange returns values only in [min, max].
71     template <typename T, typename Min, typename Max>
72     void nextRange(T*, Min, Max);
73 
74     // nextEnum is a wrapper around nextRange for enums.
75     template <typename T>
76     void nextEnum(T* ptr, T max);
77 
78     // nextN loads n * sizeof(T) bytes into ptr
79     template <typename T>
80     void nextN(T* ptr, int n);
81 
signalBug()82     void signalBug() {
83         // Tell the fuzzer that these inputs found a bug.
84         SkDebugf("Signal bug\n");
85         raise(SIGSEGV);
86     }
87 
88     // Specialized versions for when true random doesn't quite make sense
89     void next(bool* b);
90     void next(SkRegion* region);
91 
nextBool()92     bool nextBool() {
93         bool b;
94         this->next(&b);
95         return b;
96     }
97 
98     void nextRange(float* f, float min, float max);
99 
100 private:
101     template <typename T>
102     T nextT();
103 
104     const uint8_t *fData;
105     size_t fSize;
106     size_t fNextByte;
107     friend void fuzz__MakeEncoderCorpus(Fuzz*);
108 
109     void nextBytes(void* ptr, size_t size);
110 };
111 
112 template <typename Arg, typename... Args>
next(Arg * first,Args...rest)113 inline void Fuzz::next(Arg* first, Args... rest) {
114    this->next(first);
115    this->next(rest...);
116 }
117 
118 template <typename T, typename Min, typename Max>
nextRange(T * value,Min min,Max max)119 inline void Fuzz::nextRange(T* value, Min min, Max max) {
120     // UBSAN worries if we make an enum with out of range values, even temporarily.
121     using Raw = typename sk_strip_enum<T>::type;
122     Raw raw;
123     this->next(&raw);
124 
125     if (raw < (Raw)min) { raw = (Raw)min; }
126     if (raw > (Raw)max) { raw = (Raw)max; }
127     *value = (T)raw;
128 }
129 
130 template <typename T>
nextEnum(T * value,T max)131 inline void Fuzz::nextEnum(T* value, T max) {
132     // This works around the fact that UBSAN will assert if we put an invalid
133     // value into an enum. We might see issues with enums being represented
134     // on Windows differently than Linux, but that's not a thing we can fix here.
135     using U = typename std::underlying_type<T>::type;
136     U v;
137     this->next(&v);
138     if (v < (U)0) { *value = (T)0; return;}
139     if (v > (U)max) { *value = (T)max; return;}
140     *value = (T)v;
141 }
142 
143 template <typename T>
nextN(T * ptr,int n)144 inline void Fuzz::nextN(T* ptr, int n) {
145    for (int i = 0; i < n; i++) {
146        this->next(ptr+i);
147    }
148 }
149 
150 struct Fuzzable {
151     const char* name;
152     void (*fn)(Fuzz*);
153 };
154 
155 // Not static so that we can link these into oss-fuzz harnesses if we like.
156 #define DEF_FUZZ(name, f)                                               \
157     void fuzz_##name(Fuzz*);                                            \
158     sk_tools::Registry<Fuzzable> register_##name({#name, fuzz_##name}); \
159     void fuzz_##name(Fuzz* f)
160 
161 #endif  // Fuzz_DEFINED
162