OLD | NEW |
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 = ⊂ |
| 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 Loading... |
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 |
OLD | NEW |