diff --git a/projects/giflib/ProtoToGif.cpp b/projects/giflib/ProtoToGif.cpp index 2622238f3990..ab023bd93d92 100644 --- a/projects/giflib/ProtoToGif.cpp +++ b/projects/giflib/ProtoToGif.cpp @@ -3,6 +3,10 @@ using namespace gifProtoFuzzer; using namespace std; +constexpr unsigned char ProtoConverter::m_sig[]; +constexpr unsigned char ProtoConverter::m_ver89a[]; +constexpr unsigned char ProtoConverter::m_ver87a[]; + string ProtoConverter::gifProtoToString(GifProto const& proto) { visit(proto); @@ -20,10 +24,23 @@ void ProtoConverter::visit(GifProto const& gif) visit(gif.trailer()); } -void ProtoConverter::visit(Header const&) +void ProtoConverter::visit(Header const& header) { - const unsigned char header[] = {0x47,0x49,0x46,0x38,0x39,0x61}; - m_output.write((const char*)header, sizeof(header)); + // Signature GIF + m_output.write((const char*)m_sig, sizeof(m_sig)); + + switch (header.ver()) { + case Header::ENA: + m_output.write((const char*) m_ver89a, sizeof(m_ver89a)); + break; + case Header::ESA: + m_output.write((const char*) m_ver87a, sizeof(m_ver87a)); + break; + // We simply don't write anything if it's an invalid version + // Bytes that follow (LSD) will be interpreted as version + case Header::INV: + break; + } } void ProtoConverter::visit(LogicalScreenDescriptor const& lsd) diff --git a/projects/giflib/ProtoToGif.h b/projects/giflib/ProtoToGif.h index 417342581ca1..884970fad6ae 100644 --- a/projects/giflib/ProtoToGif.h +++ b/projects/giflib/ProtoToGif.h @@ -42,5 +42,9 @@ namespace gifProtoFuzzer { bool m_hasLCT = false; uint8_t m_globalColorExp = 0; uint8_t m_localColorExp = 0; + + static constexpr unsigned char m_sig[] = {0x47, 0x49, 0x46}; + static constexpr unsigned char m_ver89a[] = {0x38, 0x39, 0x61}; + static constexpr unsigned char m_ver87a[] = {0x38, 0x37, 0x61}; }; } \ No newline at end of file diff --git a/projects/giflib/build.sh b/projects/giflib/build.sh index cafac857178f..101b623ea93c 100755 --- a/projects/giflib/build.sh +++ b/projects/giflib/build.sh @@ -11,13 +11,13 @@ done ar rc libgif.a *.o cd $SRC -$CXX $CFLAGS $LIB_FUZZING_ENGINE -Wall -c -I giflib-code dgif_target.cc -o dgif_target.o +$CXX $CFLAGS -Wall -c -I giflib-code dgif_target.cc -o dgif_target.o $CXX $CXXFLAGS $LIB_FUZZING_ENGINE -std=c++11 -I giflib-code dgif_fuzz_common.cc dgif_target.o \ -o $OUT/dgif_target giflib-code/libgif.a rm -rf genfiles && mkdir genfiles && LPM/external.protobuf/bin/protoc gif_fuzz_proto.proto --cpp_out=genfiles -$CXX $CXXFLAGS $LIB_FUZZING_ENGINE -Wall -c -I giflib-code dgif_protobuf_target.cc -I libprotobuf-mutator/ \ +$CXX $CXXFLAGS -Wall -c -I giflib-code dgif_protobuf_target.cc -I libprotobuf-mutator/ \ -I genfiles \ -I LPM/external.protobuf/include \ -o dgif_protobuf_target.o diff --git a/projects/giflib/dgif_fuzz_common.cc b/projects/giflib/dgif_fuzz_common.cc index 3b404d9b03ad..b7e4b6d34ee4 100644 --- a/projects/giflib/dgif_fuzz_common.cc +++ b/projects/giflib/dgif_fuzz_common.cc @@ -1,4 +1,9 @@ #include "dgif_fuzz_common.h" +#include + +using namespace std; + +extern "C" void PrintGifError(int ErrorCode); int stub_input_reader (GifFileType *gifFileType, GifByteType *gifByteType, int len) { struct gifUserData *gud = (struct gifUserData *)gifFileType->UserData; @@ -60,4 +65,113 @@ int fuzz_dgif_extended(const uint8_t *Data, size_t Size) DGifCloseFile(GifFile, &Error); free(gifData); return 0; -} \ No newline at end of file +} + +static Color8888 gifColorToColor8888(const GifColorType& color) { + return ARGB_TO_COLOR8888(0xff, color.Red, color.Green, color.Blue); +} + +static bool willBeCleared(const GraphicsControlBlock& gcb) { + return gcb.DisposalMode == DISPOSE_BACKGROUND || gcb.DisposalMode == DISPOSE_PREVIOUS; +} + +static long getDelayMs(GraphicsControlBlock& gcb) { + return gcb.DelayTime * 10; +} + +int fuzz_dgif_ala_android(const uint8_t *Data, size_t Size) +{ + GifFileType *GifFile; + int Error; + uint8_t *gifData = (uint8_t *)malloc(Size); + memcpy(gifData, Data, Size); + struct gifUserData gUData = {Size, gifData}; + + GifFile = DGifOpen((void *)&gUData, stub_input_reader, &Error); + if (GifFile == NULL) { + free(gifData); + return 0; + } + + if(DGifSlurp(GifFile) != GIF_OK){ + PrintGifError(GifFile->Error); + DGifCloseFile(GifFile, &Error); + free(gifData); + return 0; + } + + long durationMs = 0; + int lastUnclearedFrame = -1; + bool *preservedFrames = new bool[GifFile->ImageCount]; + int *restoringFrames = new int[GifFile->ImageCount]; + int loopCount = 0; + Color8888 bgColor = 0; + + GraphicsControlBlock gcb; + for (int i = 0; i < GifFile->ImageCount; i++) { + const SavedImage& image = GifFile->SavedImages[i]; + // find the loop extension pair + for (int j = 0; (j + 1) < image.ExtensionBlockCount; j++) { + ExtensionBlock* eb1 = image.ExtensionBlocks + j; + ExtensionBlock* eb2 = image.ExtensionBlocks + j + 1; + if (eb1->Function == APPLICATION_EXT_FUNC_CODE + // look for "NETSCAPE2.0" app extension + && eb1->ByteCount == 11 + && !memcmp((const char*)(eb1->Bytes), "NETSCAPE2.0", 11) + // verify extension contents and get loop count + && eb2->Function == CONTINUE_EXT_FUNC_CODE + && eb2->ByteCount == 3 + && eb2->Bytes[0] == 1) { + loopCount = (int)(eb2->Bytes[2] << 8) + (int)(eb2->Bytes[1]); + } + } + DGifSavedExtensionToGCB(GifFile, i, &gcb); + // timing + durationMs += getDelayMs(gcb); + // preserve logic + preservedFrames[i] = false; + restoringFrames[i] = -1; + if (gcb.DisposalMode == DISPOSE_PREVIOUS && lastUnclearedFrame >= 0) { + preservedFrames[lastUnclearedFrame] = true; + restoringFrames[i] = lastUnclearedFrame; + } + if (!willBeCleared(gcb)) { + lastUnclearedFrame = i; + } + // Draw + // assert(y+8 <= Image->ImageDesc.Height); + // assert(x+8*strlen(legend) <= Image->ImageDesc.Width); + int imgHeight = GifFile->SavedImages[i].ImageDesc.Height; + int imgWidth = GifFile->SavedImages[i].ImageDesc.Width; + // TODO: Source x,y, string, and color from fuzzer input + int x, y = 0; + int strLen = 6; + if (y+8 <= imgHeight && x+8*strLen <= imgWidth) + GifDrawText8x8(&GifFile->SavedImages[i], 0, 0, "legend", 42); + } +#if GIF_DEBUG + ALOGD("FrameSequence_gif created with size %d %d, frames %d dur %ld", + GifFile->SWidth, GifFile->SHeight, GifFile->ImageCount, durationMs); + for (int i = 0; i < GifFile->ImageCount; i++) { + DGifSavedExtensionToGCB(GifFile, i, &gcb); + ALOGD(" Frame %d - must preserve %d, restore point %d, trans color %d", + i, preservedFrames[i], restoringFrames[i], gcb.TransparentColor); + } +#endif + const ColorMapObject* cmap = GifFile->SColorMap; + if (cmap) { + // calculate bg color + GraphicsControlBlock gcb; + DGifSavedExtensionToGCB(GifFile, 0, &gcb); + if (gcb.TransparentColor == NO_TRANSPARENT_COLOR + && GifFile->SBackGroundColor < cmap->ColorCount) { + bgColor = gifColorToColor8888(cmap->Colors[GifFile->SBackGroundColor]); + } + } + + DGifCloseFile(GifFile, &Error); + free(gifData); + delete[] preservedFrames; + delete[] restoringFrames; + return 0; +} diff --git a/projects/giflib/dgif_fuzz_common.h b/projects/giflib/dgif_fuzz_common.h index 2682766045a5..b5b011144cc2 100644 --- a/projects/giflib/dgif_fuzz_common.h +++ b/projects/giflib/dgif_fuzz_common.h @@ -3,12 +3,17 @@ #include #include +#define ARGB_TO_COLOR8888(a, r, g, b) \ + ((a) << 24 | (b) << 16 | (g) << 8 | (r)) + +typedef uint32_t Color8888; + struct gifUserData { size_t gifLen; uint8_t *gifData; }; - int stub_input_reader (GifFileType *gifFileType, GifByteType *gifByteType, int len); int fuzz_dgif(const uint8_t *Data, size_t Size); -int fuzz_dgif_extended(const uint8_t *Data, size_t Size); \ No newline at end of file +int fuzz_dgif_extended(const uint8_t *Data, size_t Size); +int fuzz_dgif_ala_android(const uint8_t *Data, size_t Size); \ No newline at end of file diff --git a/projects/giflib/gif_fuzz_proto.proto b/projects/giflib/gif_fuzz_proto.proto index 23dae681fc37..53c32570ef56 100644 --- a/projects/giflib/gif_fuzz_proto.proto +++ b/projects/giflib/gif_fuzz_proto.proto @@ -67,7 +67,15 @@ message CommentExtension { repeated SubBlock subs = 1; } -message Header {} +message Header { + enum Version { + ENA = 1; + ESA = 2; + INV = 3; + } + required Version ver = 1; +} + message Trailer {} message ImageChunk { @@ -82,6 +90,8 @@ message ImageChunk { message GifProto { required Header header = 1; required LogicalScreenDescriptor lsd = 2; + // Instead of making GCT optional here, we condition its visit on LSD's packed byte + // in the converter required GlobalColorTable gct = 3; repeated ImageChunk chunks = 4; required Trailer trailer = 5;