Project: openexr (academysoftwarefoundation/openexr), language: c++
Vulnerability instance: openexr.cve-2017-12596
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/openexr/openexr/issues/238
## Description:
Issue: openexr/openexr#238
Title:  heap-based buffer overflow in exrmaketiled 
State: closed
Created by: xiaoqx
Created at: 2017-07-29 06:35:23+00:00
Labels: CVE

Issue Body:
A heap-based buffer overflow occurs when we test **exrmaketiled** with the latest code of  this repo.
The details of the bug as follows:

$ ./exrmaketiled ./bug-testcase/185-openexr-heapoverflow /tmp/out
=================================================================
==18567==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x63100003c7fe at pc 0x7fc23e95a5ab bp 0x7ffe0428e7e0 sp 0x7ffe0428e7d8
READ of size 2 at 0x63100003c7fe thread T0
    #0 0x7fc23e95a5aa in hufDecode ../../openexr/OpenEXR/IlmImf/ImfHuf.cpp:898
    #1 0x7fc23e95a5aa in Imf_2_2::hufUncompress(char const*, int, unsigned short*, int) ../../openexr/OpenEXR/IlmImf/ImfHuf.cpp:1101
    #2 0x7fc23e971ca7 in Imf_2_2::PizCompressor::uncompress(char const*, int, Imath_2_2::Box<Imath_2_2::Vec2<int> >, char const*&) ../../openexr/OpenEXR/IlmImf/ImfPizCompressor.cpp:576
    #3 0x7fc23e974663 in Imf_2_2::PizCompressor::uncompress(char const*, int, int, char const*&) ../../openexr/OpenEXR/IlmImf/ImfPizCompressor.cpp:288
    #4 0x7fc23ea66bb7 in execute ../../openexr/OpenEXR/IlmImf/ImfScanLineInputFile.cpp:544
    #5 0x7fc23d4081ea in IlmThread_2_2::ThreadPool::addTask(IlmThread_2_2::Task*) ../../openexr/IlmBase/IlmThread/IlmThreadPool.cpp:433
    #6 0x7fc23ea7c136 in Imf_2_2::ScanLineInputFile::readPixels(int, int) ../../openexr/OpenEXR/IlmImf/ImfScanLineInputFile.cpp:1617
    #7 0x7fc23e904c7c in Imf_2_2::InputFile::readPixels(int, int) ../../openexr/OpenEXR/IlmImf/ImfInputFile.cpp:815
    #8 0x4236ed in makeTiled(char const*, char const*, int, Imf_2_2::LevelMode, Imf_2_2::LevelRoundingMode, Imf_2_2::Compression, int, int, std::set<std::string, std::less<std::string>, std::allocator<std::string> > const&, Extrapolation, Extrapolation, bool) ../../openexr/OpenEXR/exrmaketiled/makeTiled.cpp:572
    #9 0x405283 in main ../../openexr/OpenEXR/exrmaketiled/main.cpp:426
    #10 0x7fc23dd55f44 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21f44)
    #11 0x40692c (/data/xqx/tests/openexr-test/aflbuild/build-openexr/install/bin/exrmaketiled+0x40692c)

0x63100003c7fe is located 2 bytes to the left of 76800-byte region [0x63100003c800,0x63100004f400)
allocated by thread T0 here:
    #0 0x7fc23f54a27f in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.1+0x5527f)
    #1 0x7fc23e96adf0 in Imf_2_2::PizCompressor::PizCompressor(Imf_2_2::Header const&, unsigned long, unsigned long) ../../openexr/OpenEXR/IlmImf/ImfPizCompressor.cpp:194

SUMMARY: AddressSanitizer: heap-buffer-overflow ../../openexr/OpenEXR/IlmImf/ImfHuf.cpp:898 hufDecode
Shadow bytes around the buggy address:
  0x0c627ffff8a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c627ffff8b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c627ffff8c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c627ffff8d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c627ffff8e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c627ffff8f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa[fa]
  0x0c627ffff900: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c627ffff910: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c627ffff920: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c627ffff930: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c627ffff940: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Contiguous container OOB:fc
  ASan internal:           fe
==18567==ABORTING

And you could get the testcase and more details:

https://github.com/xiaoqx/pocs/blob/master/185-openexr-heapoverflow
https://github.com/xiaoqx/pocs/blob/master/openexr.md


Comments:
Comment by carnil on 2017-08-07 10:13:57+00:00:
This issue has been assigned CVE-2017-12596

---

Comment by carnil on 2017-09-30 14:58:34+00:00:
Any news on this issue?

---

Comment by agx on 2017-11-29 15:30:00+00:00:
This issue seems to be fixed with commit f09f5f26c1924c4f7e183428ca79c9881afaf53c (which is in master but not in any release as far as I can tell)

---

Comment by agx on 2017-11-29 15:35:17+00:00:
For reference I found this with bisecting like

```
#!/bin/bash

set -e

export CFLAGS="-fno-omit-frame-pointer -fsanitize=address -g"
export CXXFLAGS="$CFLAGS"

cd /foo/openexr
git clean -dfx
cd OpenEXR
./bootstrap
./configure 
make V=1 -j8

cd /foo/crashers/CVE-2017-12596
rm -f bla.out
../../openexr/OpenEXR/exrmaketiled/exrmaketiled ./185-openexr-heapoverflow ./foo
 >& bla.out || true

# Good and bad are reversed since master is fixed and
# we want to find the first version that has the CVE fixed.
if grep -qs AddressSanitizer ./bla.out; then
   echo "CVE-2017-12596  stil present"
   exit 0
else
   echo "CVE-2017-12596 not present"
   exit 1
fi


---

Comment by cary-ilm on 2019-06-27 00:58:14+00:00:
Looking into the OpenEXR backlog... Commit f09f5f2 was included in the 2.3.0 release, issue has been resolved.

Commit References:
f09f5f26c1924c4f7e183428ca79c9881afaf53c

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
cmake $SRC/openexr ${CMAKE_SETTINGS[@]}
make -j$(nproc)

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y make autoconf automake libtool zlib1g-dev
RUN git clone https://github.com/academysoftwarefoundation/openexr openexr
RUN git -C openexr checkout e98d25b30138634be20d925d7fe6d109311b2a06
WORKDIR $SRC/openexr
COPY build.sh $SRC/

Additional files:
filename: openexr_deepscanlines_fuzzer.cc
content: // Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <vector>

#include <ImfArray.h>
#include <ImfChannelList.h>
#include <ImfDeepFrameBuffer.h>
#include <ImfDeepScanLineInputPart.h>
#include <ImfMultiPartInputFile.h>
#include <ImfNamespace.h>

namespace IMF = OPENEXR_IMF_NAMESPACE;
using namespace IMF;
using IMATH_NAMESPACE::Box2i;
using IMATH_NAMESPACE::V2i;

namespace {

const int width = 90;
const int height = 80;
const int minX = 10;
const int minY = 11;
const Box2i dataWindow(V2i(minX, minY),
                       V2i(minX + width - 1, minY + height - 1));
const Box2i displayWindow(V2i(0, 0), V2i(minX + width * 2, minY + height * 2));

template <typename T>
static void readFile(T *inpart) {
  const Header &fileHeader = inpart->header();

  int channelCount = 0;
  for (ChannelList::ConstIterator i = fileHeader.channels().begin();
       i != fileHeader.channels().end(); ++i, ++channelCount) {
  }

  Array2D<unsigned int> localSampleCount;
  localSampleCount.resizeErase(height, width);
  Array<Array2D<void *> > data(channelCount);

  for (int i = 0; i < channelCount; i++) data[i].resizeErase(height, width);

  DeepFrameBuffer frameBuffer;

  frameBuffer.insertSampleCountSlice(
      Slice(IMF::UINT,
            (char *)(&localSampleCount[0][0] - dataWindow.min.x -
                     dataWindow.min.y * width),
            sizeof(unsigned int) * 1, sizeof(unsigned int) * width));

  std::vector<int> read_channel(channelCount);

  for (int i = 0; i < channelCount; i++) {
    PixelType type = IMF::FLOAT;

    std::stringstream ss;
    ss << i;
    std::string str = ss.str();

    int sampleSize = sizeof(float);

    int pointerSize = sizeof(char *);

    frameBuffer.insert(
        str, DeepSlice(type,
                       (char *)(&data[i][0][0] - dataWindow.min.x -
                                dataWindow.min.y * width),
                       pointerSize * 1, pointerSize * width, sampleSize));
  }

  inpart->setFrameBuffer(frameBuffer);
  inpart->readPixelSampleCounts(dataWindow.min.y, dataWindow.max.y);
  for (int i = 0; i < dataWindow.max.y - dataWindow.min.y + 1; i++) {
    int y = i + dataWindow.min.y;

    for (int j = 0; j < width; j++) {
      for (int k = 0; k < channelCount; k++) {
        data[k][i][j] = new float[localSampleCount[i][j]];
      }
    }
  }
  try {
    inpart->readPixels(dataWindow.min.y, dataWindow.max.y);
  } catch (...) {
  }

  for (int i = 0; i < height; i++) {
    for (int j = 0; j < width; j++) {
      for (int k = 0; k < channelCount; k++) {
        delete[](float *) data[k][i][j];
      }
    }
  }
}

static void readFileSingle(const char filename[]) {
  DeepScanLineInputFile *file = NULL;
  try {
    file = new DeepScanLineInputFile(filename, 0);
  } catch (...) {
    return;
  }

  try {
    readFile(file);
  } catch (std::exception &e) {
  }

  delete file;
}

static void readFileMulti(const char filename[]) {
  MultiPartInputFile *file = NULL;
  try {
    file = new MultiPartInputFile(filename, 0);
  } catch (...) {
    return;
  }

  for (int p = 0; p < file->parts(); p++) {
    DeepScanLineInputPart *inpart = NULL;
    try {
      inpart = new DeepScanLineInputPart(*file, p);
    } catch (...) {
      continue;
    }
    try {
      readFile(inpart);
    } catch (std::exception &e) {
    }
    delete inpart;
  }

  delete file;
}

}  // namespace

// from cl/164883104
static char *buf_to_file(const char *buf, size_t size) {
  char *name = strdup("/dev/shm/fuzz-XXXXXX");
  int fd = mkstemp(name);
  if (fd < 0) {
    perror("open");
    exit(1);
  }
  size_t pos = 0;
  while (pos < size) {
    int nbytes = write(fd, &buf[pos], size - pos);
    if (nbytes <= 0) {
      perror("write");
      exit(1);
    }
    pos += nbytes;
  }
  if (close(fd) != 0) {
    perror("close");
    exit(1);
  }
  return name;
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  char *file = buf_to_file((const char *)data, size);
  readFileSingle(file);
  readFileMulti(file);
  unlink(file);
  free(file);
  return 0;
}

filename: openexr_deeptiles_fuzzer.cc
content: // Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <ImfArray.h>
#include <ImfChannelList.h>
#include <ImfDeepTiledInputFile.h>
#include <ImfDeepTiledInputPart.h>
#include <ImfTiledInputPart.h>

namespace IMF = OPENEXR_IMF_NAMESPACE;
using namespace IMF;
using IMATH_NAMESPACE::Box2i;

namespace {

template <typename T>
static void readFile(T *part) {
  const Header &fileHeader = part->header();

  Array2D<unsigned int> localSampleCount;

  Box2i dataWindow = fileHeader.dataWindow();

  int height = dataWindow.size().y + 1;
  int width = dataWindow.size().x + 1;

  localSampleCount.resizeErase(height, width);

  int channelCount = 0;
  for (ChannelList::ConstIterator i = fileHeader.channels().begin();
       i != fileHeader.channels().end(); ++i, channelCount++) {
  }

  Array<Array2D<void *> > data(channelCount);

  for (int i = 0; i < channelCount; i++) {
    data[i].resizeErase(height, width);
  }

  DeepFrameBuffer frameBuffer;

  int memOffset = dataWindow.min.x + dataWindow.min.y * width;
  frameBuffer.insertSampleCountSlice(
      Slice(IMF::UINT, (char *)(&localSampleCount[0][0] - memOffset),
            sizeof(unsigned int) * 1, sizeof(unsigned int) * width));

  for (int i = 0; i < channelCount; i++) {
    std::stringstream ss;
    ss << i;
    std::string str = ss.str();

    int sampleSize = sizeof(float);

    int pointerSize = sizeof(char *);

    frameBuffer.insert(
        str, DeepSlice(IMF::FLOAT, (char *)(&data[i][0][0] - memOffset),
                       pointerSize * 1, pointerSize * width, sampleSize));
  }

  part->setFrameBuffer(frameBuffer);
  for (int ly = 0; ly < part->numYLevels(); ly++) {
    for (int lx = 0; lx < part->numXLevels(); lx++) {
      Box2i dataWindowL = part->dataWindowForLevel(lx, ly);

      part->readPixelSampleCounts(0, part->numXTiles(lx) - 1, 0,
                                  part->numYTiles(ly) - 1, lx, ly);

      for (int i = 0; i < part->numYTiles(ly); i++) {
        for (int j = 0; j < part->numXTiles(lx); j++) {
          Box2i box = part->dataWindowForTile(j, i, lx, ly);
          for (int y = box.min.y; y <= box.max.y; y++)
            for (int x = box.min.x; x <= box.max.x; x++) {
              int dwy = y - dataWindowL.min.y;
              int dwx = x - dataWindowL.min.x;

              for (int k = 0; k < channelCount; k++) {
                data[k][dwy][dwx] = new float[localSampleCount[dwy][dwx]];
              }
            }
        }
      }

      try {
        part->readTiles(0, part->numXTiles(lx) - 1, 0, part->numYTiles(ly) - 1,
                        lx, ly);
      } catch (...) {
      }

      for (int i = 0; i < part->levelHeight(ly); i++) {
        for (int j = 0; j < part->levelWidth(lx); j++) {
          for (int k = 0; k < channelCount; k++) {
            delete[](float *) data[k][i][j];
          }
        }
      }
    }
  }
}

static void readFileSingle(const char filename[]) {
  DeepTiledInputFile *file;
  try {
    file = new DeepTiledInputFile(filename, 8);
  } catch (...) {
    return;
  }

  try {
    readFile(file);
  } catch (std::exception &e) {
  }

  delete file;
}

static void readFileMulti(const char filename[]) {
  MultiPartInputFile *file;

  try {
    file = new MultiPartInputFile(filename, 8);
  } catch (...) {
    return;
  }

  for (int p = 0; p < file->parts(); p++) {
    DeepTiledInputPart *part;
    try {
      part = new DeepTiledInputPart(*file, p);
    } catch (...) {
      continue;
    }

    try {
      readFile(part);
    } catch (...) {
    }

    delete part;
  }

  delete file;
}

}  // namespace

// from cl/164883104
static char *buf_to_file(const char *buf, size_t size) {
  char *name = strdup("/dev/shm/fuzz-XXXXXX");
  int fd = mkstemp(name);
  if (fd < 0) {
    perror("open");
    exit(1);
  }
  size_t pos = 0;
  while (pos < size) {
    int nbytes = write(fd, &buf[pos], size - pos);
    if (nbytes <= 0) {
      perror("write");
      exit(1);
    }
    pos += nbytes;
  }
  if (close(fd) != 0) {
    perror("close");
    exit(1);
  }
  return name;
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  char *file = buf_to_file((const char *)data, size);
  Header::setMaxImageSize(10000, 10000);
  readFileSingle(file);
  readFileMulti(file);
  unlink(file);
  free(file);
  return 0;
}

filename: openexr_scanlines_fuzzer.cc
content: // Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <ImfArray.h>
#include <ImfInputPart.h>
#include <ImfMultiPartInputFile.h>
#include <ImfRgbaFile.h>

using namespace OPENEXR_IMF_INTERNAL_NAMESPACE;
using IMATH_NAMESPACE::Box2i;

namespace {

static void readSingle(const char fileName[]) {
  RgbaInputFile *in = NULL;
  try {
    in = new RgbaInputFile(fileName);
  } catch (...) {
    return;
  }

  try {
    const Box2i &dw = in->dataWindow();

    int w = dw.max.x - dw.min.x + 1;
    int dx = dw.min.x;

    if (w > (1 << 24)) return;

    Array<Rgba> pixels(w);
    in->setFrameBuffer(&pixels[-dx], 1, 0);

    for (int y = dw.min.y; y <= dw.max.y; ++y) in->readPixels(y);
  } catch (...) {
  }

  delete in;
}

static void readMulti(const char fileName[]) {
  MultiPartInputFile *file;
  try {
    file = new MultiPartInputFile(fileName);
  } catch (...) {
    return;
  }

  for (int p = 0; p < file->parts(); p++) {
    InputPart *in;
    try {
      in = new InputPart(*file, p);
    } catch (...) {
      continue;
    }

    try {
      const Box2i &dw = in->header().dataWindow();

      int w = dw.max.x - dw.min.x + 1;
      int dx = dw.min.x;

      if (w > (1 << 24)) return;

      Array<Rgba> pixels(w);
      FrameBuffer i;
      i.insert("R", Slice(HALF, (char *)&(pixels[-dx].r), sizeof(Rgba), 0));
      i.insert("G", Slice(HALF, (char *)&(pixels[-dx].g), sizeof(Rgba), 0));
      i.insert("B", Slice(HALF, (char *)&(pixels[-dx].b), sizeof(Rgba), 0));
      i.insert("A", Slice(HALF, (char *)&(pixels[-dx].a), sizeof(Rgba), 0));

      in->setFrameBuffer(i);
      for (int y = dw.min.y; y <= dw.max.y; ++y) in->readPixels(y);
    } catch (...) {
    }

    delete in;
  }

  delete file;
}

}  // namespace

// from cl/164883104
static char *buf_to_file(const char *buf, size_t size) {
  char *name = strdup("/dev/shm/fuzz-XXXXXX");
  int fd = mkstemp(name);
  if (fd < 0) {
    perror("open");
    exit(1);
  }
  size_t pos = 0;
  while (pos < size) {
    int nbytes = write(fd, &buf[pos], size - pos);
    if (nbytes <= 0) {
      perror("write");
      exit(1);
    }
    pos += nbytes;
  }
  if (close(fd) != 0) {
    perror("close");
    exit(1);
  }
  return name;
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  char *file = buf_to_file((const char *)data, size);
  readSingle(file);
  readMulti(file);
  unlink(file);
  free(file);
  return 0;
}

filename: openexr_tiles_fuzzer.cc
content: // Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <ImfArray.h>
#include <ImfTiledRgbaFile.h>

// Handle the case when the custom namespace is not exposed
#include <ImfChannelList.h>
#include <ImfMultiPartInputFile.h>
#include <ImfMultiPartOutputFile.h>
#include <ImfPartType.h>
#include <ImfTiledInputPart.h>
#include <ImfTiledOutputPart.h>
#include <OpenEXRConfig.h>

using namespace OPENEXR_IMF_INTERNAL_NAMESPACE;
using IMATH_NAMESPACE::Box2i;

namespace {

void readImageONE(TiledRgbaInputFile *in, int dwx, int dwy) {
  try {
    const Box2i &dw = in->dataWindow();

    int w = dw.max.x - dw.min.x + 1;
    int h = dw.max.y - dw.min.y + 1;

    Array2D<Rgba> pixels(h, w);
    in->setFrameBuffer(&pixels[-dwy][-dwx], 1, w);
    in->readTiles(0, in->numXTiles() - 1, 0, in->numYTiles() - 1);
  } catch (...) {
  }
}

void readImageONE2(const char fileName[]) {
  MultiPartInputFile *in;
  try {
    in = new MultiPartInputFile(fileName);
  } catch (...) {
    return;
  }

  TiledInputPart *inpart;
  try {
    for (int p = 0; p < in->parts(); p++) {
      try {
        inpart = new TiledInputPart(*in, p);
      } catch (...) {
        inpart = NULL;
        continue;
      }

      const Box2i &dw = inpart->header().dataWindow();

      int w = dw.max.x - dw.min.x + 1;
      int h = dw.max.y - dw.min.y + 1;
      int dwx = dw.min.x;
      int dwy = dw.min.y;

      Array2D<Rgba> pixels(h, w);
      FrameBuffer i;
      i.insert("R", Slice(HALF, (char *)&(pixels[-dwy][-dwx].r), sizeof(Rgba),
                          w * sizeof(Rgba)));
      i.insert("G", Slice(HALF, (char *)&(pixels[-dwy][-dwx].g), sizeof(Rgba),
                          w * sizeof(Rgba)));
      i.insert("B", Slice(HALF, (char *)&(pixels[-dwy][-dwx].b), sizeof(Rgba),
                          w * sizeof(Rgba)));
      i.insert("A", Slice(HALF, (char *)&(pixels[-dwy][-dwx].a), sizeof(Rgba),
                          w * sizeof(Rgba)));

      inpart->setFrameBuffer(i);
      inpart->readTiles(0, inpart->numXTiles() - 1, 0, inpart->numYTiles() - 1);

      delete inpart;
      inpart = NULL;
    }
  } catch (...) {
    delete inpart;
  }

  delete in;
}

void readImageMIP(TiledRgbaInputFile *in, int dwx, int dwy) {
  try {
    int numLevels = in->numLevels();
    Array<Array2D<Rgba> > levels2(numLevels);

    for (int level = 0; level < numLevels; ++level) {
      int levelWidth = in->levelWidth(level);
      int levelHeight = in->levelHeight(level);
      levels2[level].resizeErase(levelHeight, levelWidth);

      in->setFrameBuffer(&(levels2[level])[-dwy][-dwx], 1, levelWidth);
      in->readTiles(0, in->numXTiles(level) - 1, 0, in->numYTiles(level) - 1,
                    level);
    }
  } catch (...) {
  }
}

void readImageRIP(TiledRgbaInputFile *in, int dwx, int dwy) {
  try {
    int numXLevels = in->numXLevels();
    int numYLevels = in->numYLevels();
    Array2D<Array2D<Rgba> > levels2(numYLevels, numXLevels);

    for (int ylevel = 0; ylevel < numYLevels; ++ylevel) {
      for (int xlevel = 0; xlevel < numXLevels; ++xlevel) {
        int levelWidth = in->levelWidth(xlevel);
        int levelHeight = in->levelHeight(ylevel);
        levels2[ylevel][xlevel].resizeErase(levelHeight, levelWidth);
        in->setFrameBuffer(&(levels2[ylevel][xlevel])[-dwy][-dwx], 1,
                           levelWidth);

        in->readTiles(0, in->numXTiles(xlevel) - 1, 0,
                      in->numYTiles(ylevel) - 1, xlevel, ylevel);
      }
    }
  } catch (...) {
  }
}

}  // namespace

static void fuzzImage(const char filename[]) {
  Header::setMaxImageSize(10000, 10000);
  Header::setMaxTileSize(10000, 10000);

  TiledRgbaInputFile *in;
  try {
    in = new TiledRgbaInputFile(filename);
  } catch (...) {
    return;
  }

  const Box2i &dw = in->dataWindow();
  int dwx = dw.min.x;
  int dwy = dw.min.y;

  readImageMIP(in, dwx, dwy);
  readImageRIP(in, dwx, dwy);
  readImageONE(in, dwx, dwy);
  readImageONE2(filename);

  delete in;
}

// from cl/164883104
static char *buf_to_file(const char *buf, size_t size) {
  char *name = strdup("/dev/shm/fuzz-XXXXXX");
  int fd = mkstemp(name);
  if (fd < 0) {
    perror("open");
    exit(1);
  }
  size_t pos = 0;
  while (pos < size) {
    int nbytes = write(fd, &buf[pos], size - pos);
    if (nbytes <= 0) {
      perror("write");
      exit(1);
    }
    pos += nbytes;
  }
  if (close(fd) != 0) {
    perror("close");
    exit(1);
  }
  return name;
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  char *file = buf_to_file((const char *)data, size);
  fuzzImage(file);
  unlink(file);
  free(file);
  return 0;
}