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

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