Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1229)

Side by Side Diff: fuzz/fuzz.cpp

Issue 1698963003: Add ability to fuzz images using scaling and different modes (Closed) Base URL: https://skia.googlesource.com/skia@master
Patch Set: Add docs Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2016 Google Inc. 2 * Copyright 2016 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "Fuzz.h" 8 #include "Fuzz.h"
9 #include "SkCanvas.h" 9 #include "SkCanvas.h"
10 #include "SkCodec.h" 10 #include "SkCodec.h"
11 #include "SkCommandLineFlags.h" 11 #include "SkCommandLineFlags.h"
12 #include "SkData.h" 12 #include "SkData.h"
13 #include "SkForceLinking.h" 13 #include "SkForceLinking.h"
14 #include "SkImage.h" 14 #include "SkImage.h"
15 #include "SkImageEncoder.h" 15 #include "SkImageEncoder.h"
16 #include "SkMallocPixelRef.h" 16 #include "SkMallocPixelRef.h"
17 #include "SkPicture.h" 17 #include "SkPicture.h"
18 #include "SkStream.h" 18 #include "SkStream.h"
19 19
20 #include <signal.h> 20 #include <signal.h>
21 #include <stdlib.h> 21 #include <stdlib.h>
22 22
23 __SK_FORCE_IMAGE_DECODER_LINKING; 23 __SK_FORCE_IMAGE_DECODER_LINKING;
scroggo 2016/02/16 19:17:38 Note to self: We can remove this once we switch SK
kjlubick 2016/02/16 19:55:13 I added a TODO
24 24
25 DEFINE_string2(bytes, b, "", "A path to a file. This can be the fuzz bytes or a binary to parse."); 25 DEFINE_string2(bytes, b, "", "A path to a file. This can be the fuzz bytes or a binary to parse.");
26 DEFINE_string2(name, n, "", "If --type is 'api', fuzz the API with this name."); 26 DEFINE_string2(name, n, "", "If --type is 'api', fuzz the API with this name.");
27 27
28 DEFINE_string2(type, t, "api", "How to interpret --bytes, either 'image', 'skp', or 'api'."); 28 DEFINE_string2(type, t, "api", "How to interpret --bytes, either 'image_scale', 'image_mode', 'skp', or 'api'.");
29 DEFINE_string2(dump, d, "", "If not empty, dump 'image' or 'skp' types as a PNG with this name."); 29 DEFINE_string2(dump, d, "", "If not empty, dump 'image*' or 'skp' types as a PNG with this name.");
30 30
31 static int printUsage(const char* name) { 31 static int printUsage(const char* name) {
32 SkDebugf("Usage: %s -t <type> -b <path/to/file> [-n api-to-fuzz]\n", name); 32 SkDebugf("Usage: %s -t <type> -b <path/to/file> [-n api-to-fuzz]\n", name);
33 return 1; 33 return 1;
34 } 34 }
35 static uint8_t calculateMulti(SkData*);
scroggo 2016/02/16 19:17:38 nit: Typically we name static functions as calcul
kjlubick 2016/02/16 19:55:13 Done. option seems a bit better.
35 36
36 static int fuzz_api(SkData*); 37 static int fuzz_api(SkData*);
37 static int fuzz_img(SkData*); 38 static int fuzz_img(SkData*, uint8_t, uint8_t);
38 static int fuzz_skp(SkData*); 39 static int fuzz_skp(SkData*);
39 40
40 int main(int argc, char** argv) { 41 int main(int argc, char** argv) {
41 SkCommandLineFlags::Parse(argc, argv); 42 SkCommandLineFlags::Parse(argc, argv);
42 43
43 const char* path = FLAGS_bytes.isEmpty() ? argv[0] : FLAGS_bytes[0]; 44 const char* path = FLAGS_bytes.isEmpty() ? argv[0] : FLAGS_bytes[0];
44 SkAutoTUnref<SkData> bytes(SkData::NewFromFileName(path)); 45 SkAutoTUnref<SkData> bytes(SkData::NewFromFileName(path));
45 if (!bytes) { 46 if (!bytes) {
46 SkDebugf("Could not read %s\n", path); 47 SkDebugf("Could not read %s\n", path);
47 return 2; 48 return 2;
48 } 49 }
49 50
51 uint8_t multi = calculateMulti(bytes);
52
50 if (!FLAGS_type.isEmpty()) { 53 if (!FLAGS_type.isEmpty()) {
51 switch (FLAGS_type[0][0]) { 54 switch (FLAGS_type[0][0]) {
52 case 'a': return fuzz_api(bytes); 55 case 'a': return fuzz_api(bytes);
53 case 'i': return fuzz_img(bytes); 56 // We only allow one degree of freedom to avoid a search space explo sion for afl-fuzz.
57 case 'i': return fuzz_img(bytes, strcmp(FLAGS_type[0], "image_scale" ) != 0 ? 0: multi, strcmp(FLAGS_type[0], "image_mode") != 0 ? 0: multi);
scroggo 2016/02/16 19:17:38 Split this up over multiple lines?
kjlubick 2016/02/16 19:55:13 Done.
54 case 's': return fuzz_skp(bytes); 58 case 's': return fuzz_skp(bytes);
55 } 59 }
56 } 60 }
57 return printUsage(argv[0]); 61 return printUsage(argv[0]);
58 } 62 }
59 63
64 // This adds up the first 1024 bytes and returns it as an 8 bit integer. This a llows afl-fuzz to
65 // deterministically excercise different paths (such as different scaling sizes or different
66 // image modes) without needing to introduce a parameter. This way we don't nee d a image_scale1,
67 // image_scale2, image_scale4, image_scale8, etc fuzzer, we can just have a imag e_scale fuzzer.
68 // Clients are expected to transform this number into a different range, e.g. wi th modulo (%).
69 static uint8_t calculateMulti(SkData* bytes) {
70 uint8_t total = 0;
71 const uint8_t* data = bytes->bytes();
72 for (size_t i = 0; i < 1024 && i < bytes->size(); i++) {
73 total += data[i];
74 }
75 return total;
76 }
77
60 int fuzz_api(SkData* bytes) { 78 int fuzz_api(SkData* bytes) {
61 const char* name = FLAGS_name.isEmpty() ? "" : FLAGS_name[0]; 79 const char* name = FLAGS_name.isEmpty() ? "" : FLAGS_name[0];
62 80
63 for (auto r = SkTRegistry<Fuzzable>::Head(); r; r = r->next()) { 81 for (auto r = SkTRegistry<Fuzzable>::Head(); r; r = r->next()) {
64 auto fuzzable = r->factory(); 82 auto fuzzable = r->factory();
65 if (0 == strcmp(name, fuzzable.name)) { 83 if (0 == strcmp(name, fuzzable.name)) {
66 SkDebugf("Fuzzing %s...\n", fuzzable.name); 84 SkDebugf("Fuzzing %s...\n", fuzzable.name);
67 Fuzz fuzz(bytes); 85 Fuzz fuzz(bytes);
68 fuzzable.fn(&fuzz); 86 fuzzable.fn(&fuzz);
69 SkDebugf("[terminated] Success!\n"); 87 SkDebugf("[terminated] Success!\n");
70 return 0; 88 return 0;
71 } 89 }
72 } 90 }
73 91
74 SkDebugf("When using --type api, please choose an API to fuzz with --name/-n :\n"); 92 SkDebugf("When using --type api, please choose an API to fuzz with --name/-n :\n");
75 for (auto r = SkTRegistry<Fuzzable>::Head(); r; r = r->next()) { 93 for (auto r = SkTRegistry<Fuzzable>::Head(); r; r = r->next()) {
76 auto fuzzable = r->factory(); 94 auto fuzzable = r->factory();
77 SkDebugf("\t%s\n", fuzzable.name); 95 SkDebugf("\t%s\n", fuzzable.name);
78 } 96 }
79 return 1; 97 return 1;
80 } 98 }
81 99
82 static void dump_png(SkBitmap bitmap) { 100 static void dump_png(SkBitmap bitmap) {
83 if (!FLAGS_dump.isEmpty()) { 101 if (!FLAGS_dump.isEmpty()) {
84 SkImageEncoder::EncodeFile(FLAGS_dump[0], bitmap, SkImageEncoder::kPNG_T ype, 100); 102 SkImageEncoder::EncodeFile(FLAGS_dump[0], bitmap, SkImageEncoder::kPNG_T ype, 100);
85 SkDebugf("Dumped to %s\n", FLAGS_dump[0]); 103 SkDebugf("Dumped to %s\n", FLAGS_dump[0]);
86 } 104 }
87 } 105 }
88 106
89 int fuzz_img(SkData* bytes) { 107 int fuzz_img(SkData* bytes, uint8_t scale, uint8_t mode) {
108 // We can scale 1x, 2x, 4x, 8x, 16x
109 scale = scale % 5;
110 scale = pow(2, scale);
111 SkDebugf("Scaling factor: %d\n", scale);
112
113 // We have 4 different modes of decoding, just like DM.
114 mode = mode % 4;
115 SkDebugf("Mode: %d\n", mode);
116
117 // This is mostly copied from DMSrcSink's CodecSrc::draw method.
90 SkDebugf("Decoding\n"); 118 SkDebugf("Decoding\n");
91 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(bytes)); 119 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(bytes));
92 if (nullptr == codec.get()) { 120 if (nullptr == codec.get()) {
93 SkDebugf("[terminated] Couldn't create codec.\n"); 121 SkDebugf("[terminated] Couldn't create codec.\n");
94 return 3; 122 return 3;
95 } 123 }
96 124
97 SkImageInfo decodeInfo = codec->getInfo(); 125 SkImageInfo decodeInfo = codec->getInfo();
126
127 SkISize size = codec->getScaledDimensions(scale);
128 decodeInfo = decodeInfo.makeWH(size.width(), size.height());
129
98 // Construct a color table for the decode if necessary 130 // Construct a color table for the decode if necessary
99 SkAutoTUnref<SkColorTable> colorTable(nullptr); 131 SkAutoTUnref<SkColorTable> colorTable(nullptr);
100 SkPMColor* colorPtr = nullptr; 132 SkPMColor* colorPtr = nullptr;
101 int* colorCountPtr = nullptr; 133 int* colorCountPtr = nullptr;
102 int maxColors = 256; 134 int maxColors = 256;
103 if (kIndex_8_SkColorType == decodeInfo.colorType()) { 135 if (kIndex_8_SkColorType == decodeInfo.colorType()) {
104 SkPMColor colors[256]; 136 SkPMColor colors[256];
105 colorTable.reset(new SkColorTable(colors, maxColors)); 137 colorTable.reset(new SkColorTable(colors, maxColors));
106 colorPtr = const_cast<SkPMColor*>(colorTable->readColors()); 138 colorPtr = const_cast<SkPMColor*>(colorTable->readColors());
107 colorCountPtr = &maxColors; 139 colorCountPtr = &maxColors;
108 } 140 }
109 141
110 SkBitmap bitmap; 142 SkBitmap bitmap;
111 SkMallocPixelRef::ZeroedPRFactory zeroFactory; 143 SkMallocPixelRef::ZeroedPRFactory zeroFactory;
112 SkCodec::Options options; 144 SkCodec::Options options;
113 options.fZeroInitialized = SkCodec::kYes_ZeroInitialized; 145 options.fZeroInitialized = SkCodec::kYes_ZeroInitialized;
114 146
115 if (!bitmap.tryAllocPixels(decodeInfo, &zeroFactory, nullptr)) { 147 if (!bitmap.tryAllocPixels(decodeInfo, &zeroFactory, colorTable.get())) {
116 SkDebugf("[terminated] Could not allocate memory. Image might be too la rge (%d x %d)", 148 SkDebugf("[terminated] Could not allocate memory. Image might be too la rge (%d x %d)",
117 decodeInfo.width(), decodeInfo.height()); 149 decodeInfo.width(), decodeInfo.height());
118 return 4; 150 return 4;
119 } 151 }
120 152
121 switch (codec->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowBytes(), &options, 153 switch (mode) {
122 colorPtr, colorCountPtr)) { 154 case 0: {//kCodecZeroInit_Mode, kCodec_Mode
123 case SkCodec::kSuccess: 155 switch (codec->getPixels(decodeInfo, bitmap.getPixels(), bitmap.rowB ytes(), &options,
156 colorPtr, colorCountPtr)) {
scroggo 2016/02/16 19:17:38 nit: This should align with decodeInfo, above (or
kjlubick 2016/02/16 19:55:13 Done.
157 case SkCodec::kSuccess:
scroggo 2016/02/16 19:17:38 nit: This should be indented four more spaces.
kjlubick 2016/02/16 19:55:13 Done.
158 SkDebugf("[terminated] Success!\n");
159 break;
160 case SkCodec::kIncompleteInput:
161 SkDebugf("[terminated] Partial Success\n");
162 break;
163 case SkCodec::kInvalidConversion:
scroggo 2016/02/16 19:17:38 Is this happening? If so, this is a bug.
msarett 2016/02/16 19:54:01 Testing if we can remove this from DM. I can't re
kjlubick 2016/02/16 19:55:13 It's not, AFAIK, but I can add signalBug so we kno
164 SkDebugf("[terminated] Incompatible colortype conversion\n");
165 return 5;
166 default:
167 SkDebugf("[terminated] Couldn't getPixels.\n");
168 return 6;
169 }
170 break;
171 }
172 case 1: {//kScanline_Mode
173 if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL , colorPtr,
174 colorCountPt r)) {
scroggo 2016/02/16 19:17:38 nit: Normally we would line this up with "decodeIn
kjlubick 2016/02/16 19:55:13 Done.
175 SkDebugf("[terminated] Could not start scanline decoder\n");
176 return 7;
177 }
178
179 void* dst = bitmap.getAddr(0, 0);
180 size_t rowBytes = bitmap.rowBytes();
181 uint32_t height = decodeInfo.height();
182 switch (codec->getScanlineOrder()) {
183 case SkCodec::kTopDown_SkScanlineOrder:
184 case SkCodec::kBottomUp_SkScanlineOrder:
185 case SkCodec::kNone_SkScanlineOrder:
186 // We do not need to check the return value. On an incomple te
187 // image, memory will be filled with a default value.
188 codec->getScanlines(dst, height, rowBytes);
189 break;
190 case SkCodec::kOutOfOrder_SkScanlineOrder: {
191 for (int y = 0; y < decodeInfo.height(); y++) {
192 int dstY = codec->outputScanline(y);
193 void* dstPtr = bitmap.getAddr(0, dstY);
194 // We complete the loop, even if this call begins to fai l
195 // due to an incomplete image. This ensures any uniniti alized
196 // memory will be filled with the proper value.
197 codec->getScanlines(dstPtr, 1, bitmap.rowBytes());
198 }
199 break;
200 }
201 }
124 SkDebugf("[terminated] Success!\n"); 202 SkDebugf("[terminated] Success!\n");
125 break; 203 break;
126 case SkCodec::kIncompleteInput: 204 }
127 SkDebugf("[terminated] Partial Success\n"); 205 case 2: { //kStripe_Mode
128 break; 206 const int height = decodeInfo.height();
129 case SkCodec::kInvalidConversion: 207 // This value is chosen arbitrarily. We exercise more cases by choo sing a value that
130 SkDebugf("[terminated] Incompatible colortype conversion\n"); 208 // does not align with image blocks.
131 return 5; 209 const int stripeHeight = 37;
210 const int numStripes = (height + stripeHeight - 1) / stripeHeight;
211
212 // Decode odd stripes
213 if (SkCodec::kSuccess != codec->startScanlineDecode(decodeInfo, NULL , colorPtr,
214 colorCountPtr)
215 || SkCodec::kTopDown_SkScanlineOrder != codec->getScanlineOr der()) {
216 // This mode was designed to test the new skip scanlines API in libjpeg-turbo.
217 // Jpegs have kTopDown_SkScanlineOrder, and at this time, it is not interesting
218 // to run this test for image types that do not have this scanli ne ordering.
219 SkDebugf("[terminated] Could not start top-down scanline decoder \n");
220 return 8;
221 }
222
223 for (int i = 0; i < numStripes; i += 2) {
224 // Skip a stripe
225 const int linesToSkip = SkTMin(stripeHeight, height - i * stripe Height);
226 codec->skipScanlines(linesToSkip);
227
228 // Read a stripe
229 const int startY = (i + 1) * stripeHeight;
230 const int linesToRead = SkTMin(stripeHeight, height - startY);
231 if (linesToRead > 0) {
232 codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitmap.rowBytes());
233 }
234 }
235
236 // Decode even stripes
237 const SkCodec::Result startResult = codec->startScanlineDecode(decod eInfo, nullptr,
238 colorPtr, colorCountPtr);
239 if (SkCodec::kSuccess != startResult) {
240 SkDebugf("[terminated] Failed to restart scanline decoder with s ame parameters.\n");
241 return 9;
242 }
243 for (int i = 0; i < numStripes; i += 2) {
244 // Read a stripe
245 const int startY = i * stripeHeight;
246 const int linesToRead = SkTMin(stripeHeight, height - startY);
247 codec->getScanlines(bitmap.getAddr(0, startY), linesToRead, bitm ap.rowBytes());
248
249 // Skip a stripe
250 const int linesToSkip = SkTMin(stripeHeight, height - (i + 1) * stripeHeight);
251 if (linesToSkip > 0) {
252 codec->skipScanlines(linesToSkip);
253 }
254 }
255 SkDebugf("[terminated] Success!\n");
256 break;
257 }
258 case 3: { //kSubset_Mode
259 // Arbitrarily choose a divisor.
260 int divisor = 2;
261 // Total width/height of the image.
262 const int W = codec->getInfo().width();
263 const int H = codec->getInfo().height();
264 if (divisor > W || divisor > H) {
265 SkDebugf("[terminated] Cannot codec subset: divisor %d is too bi g "
266 "with dimensions (%d x %d) \n", divisor,
267 W, H);
268 return 10;
269 }
270 // subset dimensions
271 // SkWebpCodec, the only one that supports subsets, requires even to p/left boundaries.
272 const int w = SkAlign2(W / divisor);
273 const int h = SkAlign2(H / divisor);
274 SkIRect subset;
275 SkCodec::Options opts;
276 opts.fSubset = &subset;
277 SkBitmap subsetBm;
278 // We will reuse pixel memory from bitmap.
279 void* pixels = bitmap.getPixels();
280 // Keep track of left and top (for drawing subsetBm into canvas). We could use
281 // scale * x and scale * y, but we want integers such that the next subset will start
282 // where the last one ended. So we'll add decodeInfo.width() and hei ght().
283 int left = 0;
284 for (int x = 0; x < W; x += w) {
285 int top = 0;
286 for (int y = 0; y < H; y+= h) {
287 // Do not make the subset go off the edge of the image.
288 const int preScaleW = SkTMin(w, W - x);
289 const int preScaleH = SkTMin(h, H - y);
290 subset.setXYWH(x, y, preScaleW, preScaleH);
291 // And scale
292 // FIXME: Should we have a version of getScaledDimensions th at takes a subset
293 // into account?
294 decodeInfo = decodeInfo.makeWH(
295 SkTMax(1, SkScalarRoundToInt(preScaleW * scale)),
296 SkTMax(1, SkScalarRoundToInt(preScaleH * scale)));
297 size_t rowBytes = decodeInfo.minRowBytes();
298 if (!subsetBm.installPixels(decodeInfo, pixels, rowBytes, co lorTable.get(),
299 nullptr, nullptr)) {
300 SkDebugf("[terminated] Could not install pixels.\n");
301 return 11;
302 }
303 const SkCodec::Result result = codec->getPixels(decodeInfo, pixels, rowBytes,
304 &opts, colorPtr, colorCountPtr);
305 switch (result) {
306 case SkCodec::kSuccess:
307 case SkCodec::kIncompleteInput:
308 SkDebugf("okay\n");
309 break;
310 case SkCodec::kInvalidConversion:
311 if (0 == (x|y)) {
312 // First subset is okay to return unimplemented.
313 SkDebugf("[terminated] Incompatible colortype co nversion\n");
314 return 12;
315 }
316 // If the first subset succeeded, a later one should not fail.
317 // fall through to failure
318 case SkCodec::kUnimplemented:
319 if (0 == (x|y)) {
320 // First subset is okay to return unimplemented.
321 SkDebugf("[terminated] subset codec not supporte d\n");
322 return 13;
323 }
324 // If the first subset succeeded, why would a later one fail?
325 // fall through to failure
326 default:
327 SkDebugf("[terminated] subset codec failed to decode (%d, %d, %d, %d) "
328 "with dimensions (%d x %d)\t e rror %d\n",
329 x, y, decodeInfo.width(), deco deInfo.height(),
330 W, H, result);
331 return 14;
332 }
333 // translate by the scaled height.
334 top += decodeInfo.height();
335 }
336 // translate by the scaled width.
337 left += decodeInfo.width();
338 }
339 SkDebugf("[terminated] Success!\n");
340 break;
341 }
132 default: 342 default:
133 // Everything else is considered a failure. 343 SkDebugf("[terminated] Mode not implemented yet\n");
134 SkDebugf("[terminated] Couldn't getPixels.\n");
135 return 6;
136 } 344 }
137 345
138 dump_png(bitmap); 346 dump_png(bitmap);
139 return 0; 347 return 0;
140 } 348 }
141 349
142 int fuzz_skp(SkData* bytes) { 350 int fuzz_skp(SkData* bytes) {
143 SkMemoryStream stream(bytes); 351 SkMemoryStream stream(bytes);
144 SkDebugf("Decoding\n"); 352 SkDebugf("Decoding\n");
145 SkAutoTUnref<SkPicture> pic(SkPicture::CreateFromStream(&stream)); 353 SkAutoTUnref<SkPicture> pic(SkPicture::CreateFromStream(&stream));
(...skipping 28 matching lines...) Expand all
174 T val; 382 T val;
175 memcpy(&val, fBytes->bytes() + fNextByte, sizeof(T)); 383 memcpy(&val, fBytes->bytes() + fNextByte, sizeof(T));
176 fNextByte += sizeof(T); 384 fNextByte += sizeof(T);
177 return val; 385 return val;
178 } 386 }
179 387
180 uint8_t Fuzz::nextB() { return this->nextT<uint8_t >(); } 388 uint8_t Fuzz::nextB() { return this->nextT<uint8_t >(); }
181 uint32_t Fuzz::nextU() { return this->nextT<uint32_t>(); } 389 uint32_t Fuzz::nextU() { return this->nextT<uint32_t>(); }
182 float Fuzz::nextF() { return this->nextT<float >(); } 390 float Fuzz::nextF() { return this->nextT<float >(); }
183 391
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698