Loading native/graphics/jni/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -111,6 +111,7 @@ cc_defaults { "allocator_may_return_null = 1", ], }, dictionary: "fuzz/imagedecoder_fuzzer.dict", host_supported: true, } Loading native/graphics/jni/fuzz/fuzz_imagedecoder.cpp +91 −50 Original line number Diff line number Diff line Loading @@ -15,32 +15,15 @@ */ #include <android/imagedecoder.h> #include <binder/IPCThreadState.h> #include <stddef.h> #include <stdint.h> #include <cstdlib> #include <memory> #include <fuzzer/FuzzedDataProvider.h> #ifdef PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR #include <fuzz/png_mutator.h> #endif struct DecoderDeleter { void operator()(AImageDecoder* decoder) const { AImageDecoder_delete(decoder); } }; using DecoderPointer = std::unique_ptr<AImageDecoder, DecoderDeleter>; static DecoderPointer makeDecoder(const uint8_t* data, size_t size) { AImageDecoder* decoder = nullptr; int result = AImageDecoder_createFromBuffer(data, size, &decoder); if (result != ANDROID_IMAGE_DECODER_SUCCESS) { // This was not a valid image. return nullptr; } return DecoderPointer(decoder); } constexpr int32_t kMaxDimension = 5000; constexpr int32_t kMinDimension = 0; struct PixelFreer { void operator()(void* pixels) const { std::free(pixels); } Loading @@ -49,40 +32,98 @@ struct PixelFreer { using PixelPointer = std::unique_ptr<void, PixelFreer>; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // Without this call, decoding HEIF may time out on binder IPC calls. android::ProcessState::self()->startThreadPool(); DecoderPointer decoder = makeDecoder(data, size); FuzzedDataProvider dataProvider = FuzzedDataProvider(data, size); /** * Use maximum of 80% of buffer for creating decoder and save at least * 20% buffer for fuzzing other APIs */ const int32_t dataSize = dataProvider.ConsumeIntegralInRange<int32_t>(0, (size * 80) / 100); std::vector<uint8_t> inputBuffer = dataProvider.ConsumeBytes<uint8_t>(dataSize); AImageDecoder* decoder = nullptr; AImageDecoder_createFromBuffer(inputBuffer.data(), inputBuffer.size(), &decoder); if (!decoder) { return 0; } const AImageDecoderHeaderInfo* info = AImageDecoder_getHeaderInfo(decoder.get()); int32_t width = AImageDecoderHeaderInfo_getWidth(info); int32_t height = AImageDecoderHeaderInfo_getHeight(info); // Set an arbitrary limit on the size of an image. The fuzzer runs with a // limited amount of memory, and keeping this allocation small allows the // fuzzer to continue running to try to find more serious problems. This // size is large enough to hold a photo taken by a current gen phone. constexpr int32_t kMaxDimension = 5000; if (width > kMaxDimension || height > kMaxDimension) { return 0; const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(decoder); AImageDecoderFrameInfo* frameInfo = AImageDecoderFrameInfo_create(); int32_t height = AImageDecoderHeaderInfo_getHeight(headerInfo); int32_t width = AImageDecoderHeaderInfo_getWidth(headerInfo); while (dataProvider.remaining_bytes()) { auto invokeImageApi = dataProvider.PickValueInArray<const std::function<void()>>({ [&]() { int32_t testHeight = dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension, kMaxDimension); int32_t testWidth = dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension, kMaxDimension); int32_t result = AImageDecoder_setTargetSize(decoder, testHeight, testWidth); if (result == ANDROID_IMAGE_DECODER_SUCCESS) { height = testHeight; width = testWidth; } size_t stride = AImageDecoder_getMinimumStride(decoder.get()); }, [&]() { const bool required = dataProvider.ConsumeBool(); AImageDecoder_setUnpremultipliedRequired(decoder, required); }, [&]() { AImageDecoder_setAndroidBitmapFormat( decoder, dataProvider.ConsumeIntegralInRange< int32_t>(ANDROID_BITMAP_FORMAT_NONE, ANDROID_BITMAP_FORMAT_RGBA_1010102) /* format */); }, [&]() { AImageDecoder_setDataSpace(decoder, dataProvider .ConsumeIntegral<int32_t>() /* dataspace */); }, [&]() { ARect rect{dataProvider.ConsumeIntegral<int32_t>() /* left */, dataProvider.ConsumeIntegral<int32_t>() /* top */, dataProvider.ConsumeIntegral<int32_t>() /* right */, dataProvider.ConsumeIntegral<int32_t>() /* bottom */}; AImageDecoder_setCrop(decoder, rect); }, [&]() { AImageDecoderHeaderInfo_getWidth(headerInfo); }, [&]() { AImageDecoderHeaderInfo_getMimeType(headerInfo); }, [&]() { AImageDecoderHeaderInfo_getAlphaFlags(headerInfo); }, [&]() { AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo); }, [&]() { int32_t tempHeight; int32_t tempWidth; AImageDecoder_computeSampledSize(decoder, dataProvider.ConsumeIntegral< int>() /* sampleSize */, &tempWidth, &tempHeight); }, [&]() { AImageDecoderHeaderInfo_getDataSpace(headerInfo); }, [&]() { AImageDecoder_getRepeatCount(decoder); }, [&]() { AImageDecoder_getFrameInfo(decoder, frameInfo); }, [&]() { AImageDecoderFrameInfo_getDuration(frameInfo); }, [&]() { AImageDecoderFrameInfo_hasAlphaWithinBounds(frameInfo); }, [&]() { AImageDecoderFrameInfo_getDisposeOp(frameInfo); }, [&]() { AImageDecoderFrameInfo_getBlendOp(frameInfo); }, [&]() { AImageDecoder_setInternallyHandleDisposePrevious( decoder, dataProvider.ConsumeBool() /* handle */); }, [&]() { AImageDecoder_rewind(decoder); }, [&]() { AImageDecoder_advanceFrame(decoder); }, [&]() { size_t stride = AImageDecoder_getMinimumStride(decoder); size_t pixelSize = height * stride; auto pixels = PixelPointer(std::malloc(pixelSize)); if (!pixels.get()) { return 0; return; } while (true) { int result = AImageDecoder_decodeImage(decoder.get(), pixels.get(), stride, pixelSize); if (result != ANDROID_IMAGE_DECODER_SUCCESS) break; result = AImageDecoder_advanceFrame(decoder.get()); if (result != ANDROID_IMAGE_DECODER_SUCCESS) break; AImageDecoder_decodeImage(decoder, pixels.get(), stride, pixelSize); }, }); invokeImageApi(); } AImageDecoderFrameInfo_delete(frameInfo); AImageDecoder_delete(decoder); return 0; } native/graphics/jni/fuzz/imagedecoder_fuzzer.dict 0 → 100644 +7 −0 Original line number Diff line number Diff line kw1="\x89\x50\x4E\x47" kw2="\xff\xD8\xFF" kw4="\x52\x49\x46\x46" kw5="\x00\x00\x01\x00" kw6="\x47\x49\x46\x08" kw7="ftyp" kw8="\x04\x00\x00\x00" Loading
native/graphics/jni/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -111,6 +111,7 @@ cc_defaults { "allocator_may_return_null = 1", ], }, dictionary: "fuzz/imagedecoder_fuzzer.dict", host_supported: true, } Loading
native/graphics/jni/fuzz/fuzz_imagedecoder.cpp +91 −50 Original line number Diff line number Diff line Loading @@ -15,32 +15,15 @@ */ #include <android/imagedecoder.h> #include <binder/IPCThreadState.h> #include <stddef.h> #include <stdint.h> #include <cstdlib> #include <memory> #include <fuzzer/FuzzedDataProvider.h> #ifdef PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR #include <fuzz/png_mutator.h> #endif struct DecoderDeleter { void operator()(AImageDecoder* decoder) const { AImageDecoder_delete(decoder); } }; using DecoderPointer = std::unique_ptr<AImageDecoder, DecoderDeleter>; static DecoderPointer makeDecoder(const uint8_t* data, size_t size) { AImageDecoder* decoder = nullptr; int result = AImageDecoder_createFromBuffer(data, size, &decoder); if (result != ANDROID_IMAGE_DECODER_SUCCESS) { // This was not a valid image. return nullptr; } return DecoderPointer(decoder); } constexpr int32_t kMaxDimension = 5000; constexpr int32_t kMinDimension = 0; struct PixelFreer { void operator()(void* pixels) const { std::free(pixels); } Loading @@ -49,40 +32,98 @@ struct PixelFreer { using PixelPointer = std::unique_ptr<void, PixelFreer>; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // Without this call, decoding HEIF may time out on binder IPC calls. android::ProcessState::self()->startThreadPool(); DecoderPointer decoder = makeDecoder(data, size); FuzzedDataProvider dataProvider = FuzzedDataProvider(data, size); /** * Use maximum of 80% of buffer for creating decoder and save at least * 20% buffer for fuzzing other APIs */ const int32_t dataSize = dataProvider.ConsumeIntegralInRange<int32_t>(0, (size * 80) / 100); std::vector<uint8_t> inputBuffer = dataProvider.ConsumeBytes<uint8_t>(dataSize); AImageDecoder* decoder = nullptr; AImageDecoder_createFromBuffer(inputBuffer.data(), inputBuffer.size(), &decoder); if (!decoder) { return 0; } const AImageDecoderHeaderInfo* info = AImageDecoder_getHeaderInfo(decoder.get()); int32_t width = AImageDecoderHeaderInfo_getWidth(info); int32_t height = AImageDecoderHeaderInfo_getHeight(info); // Set an arbitrary limit on the size of an image. The fuzzer runs with a // limited amount of memory, and keeping this allocation small allows the // fuzzer to continue running to try to find more serious problems. This // size is large enough to hold a photo taken by a current gen phone. constexpr int32_t kMaxDimension = 5000; if (width > kMaxDimension || height > kMaxDimension) { return 0; const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(decoder); AImageDecoderFrameInfo* frameInfo = AImageDecoderFrameInfo_create(); int32_t height = AImageDecoderHeaderInfo_getHeight(headerInfo); int32_t width = AImageDecoderHeaderInfo_getWidth(headerInfo); while (dataProvider.remaining_bytes()) { auto invokeImageApi = dataProvider.PickValueInArray<const std::function<void()>>({ [&]() { int32_t testHeight = dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension, kMaxDimension); int32_t testWidth = dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension, kMaxDimension); int32_t result = AImageDecoder_setTargetSize(decoder, testHeight, testWidth); if (result == ANDROID_IMAGE_DECODER_SUCCESS) { height = testHeight; width = testWidth; } size_t stride = AImageDecoder_getMinimumStride(decoder.get()); }, [&]() { const bool required = dataProvider.ConsumeBool(); AImageDecoder_setUnpremultipliedRequired(decoder, required); }, [&]() { AImageDecoder_setAndroidBitmapFormat( decoder, dataProvider.ConsumeIntegralInRange< int32_t>(ANDROID_BITMAP_FORMAT_NONE, ANDROID_BITMAP_FORMAT_RGBA_1010102) /* format */); }, [&]() { AImageDecoder_setDataSpace(decoder, dataProvider .ConsumeIntegral<int32_t>() /* dataspace */); }, [&]() { ARect rect{dataProvider.ConsumeIntegral<int32_t>() /* left */, dataProvider.ConsumeIntegral<int32_t>() /* top */, dataProvider.ConsumeIntegral<int32_t>() /* right */, dataProvider.ConsumeIntegral<int32_t>() /* bottom */}; AImageDecoder_setCrop(decoder, rect); }, [&]() { AImageDecoderHeaderInfo_getWidth(headerInfo); }, [&]() { AImageDecoderHeaderInfo_getMimeType(headerInfo); }, [&]() { AImageDecoderHeaderInfo_getAlphaFlags(headerInfo); }, [&]() { AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo); }, [&]() { int32_t tempHeight; int32_t tempWidth; AImageDecoder_computeSampledSize(decoder, dataProvider.ConsumeIntegral< int>() /* sampleSize */, &tempWidth, &tempHeight); }, [&]() { AImageDecoderHeaderInfo_getDataSpace(headerInfo); }, [&]() { AImageDecoder_getRepeatCount(decoder); }, [&]() { AImageDecoder_getFrameInfo(decoder, frameInfo); }, [&]() { AImageDecoderFrameInfo_getDuration(frameInfo); }, [&]() { AImageDecoderFrameInfo_hasAlphaWithinBounds(frameInfo); }, [&]() { AImageDecoderFrameInfo_getDisposeOp(frameInfo); }, [&]() { AImageDecoderFrameInfo_getBlendOp(frameInfo); }, [&]() { AImageDecoder_setInternallyHandleDisposePrevious( decoder, dataProvider.ConsumeBool() /* handle */); }, [&]() { AImageDecoder_rewind(decoder); }, [&]() { AImageDecoder_advanceFrame(decoder); }, [&]() { size_t stride = AImageDecoder_getMinimumStride(decoder); size_t pixelSize = height * stride; auto pixels = PixelPointer(std::malloc(pixelSize)); if (!pixels.get()) { return 0; return; } while (true) { int result = AImageDecoder_decodeImage(decoder.get(), pixels.get(), stride, pixelSize); if (result != ANDROID_IMAGE_DECODER_SUCCESS) break; result = AImageDecoder_advanceFrame(decoder.get()); if (result != ANDROID_IMAGE_DECODER_SUCCESS) break; AImageDecoder_decodeImage(decoder, pixels.get(), stride, pixelSize); }, }); invokeImageApi(); } AImageDecoderFrameInfo_delete(frameInfo); AImageDecoder_delete(decoder); return 0; }
native/graphics/jni/fuzz/imagedecoder_fuzzer.dict 0 → 100644 +7 −0 Original line number Diff line number Diff line kw1="\x89\x50\x4E\x47" kw2="\xff\xD8\xFF" kw4="\x52\x49\x46\x46" kw5="\x00\x00\x01\x00" kw6="\x47\x49\x46\x08" kw7="ftyp" kw8="\x04\x00\x00\x00"