Code Monkey home page Code Monkey logo

tinyexr's Introduction

Tiny OpenEXR image library.

Example

AppVeyor build status

Coverity Scan Build Status

tinyexr is a small, single header-only library to load and save OpenEXR (.exr) images. tinyexr is written in portable C++ (no library dependency except for STL), thus tinyexr is good to embed into your application. To use tinyexr, simply copy tinyexr.h, miniz.c and miniz.h(for zlib. You can use system-installed zlib instead of miniz, or the zlib implementation included in stb_image[_write].h. Controlled with TINYEXR_USE_MINIZ and TINYEXR_USE_STB_ZLIB compile flags) into your project.

Security

TinyEXR does not use C++ exception.

TinyEXR now does not use assert from v1.0.4(2023/06/04), except for miniz's assert. (We plan to use wuff's zlib for better security and performance)

TinyEXR is fuzz tested and currently no security issues(No seg fault for any malcious/corrupted input EXR data) as of v1.0.7.

Features

Current status of tinyexr is:

  • OpenEXR v1 image
    • Scanline format
    • Tiled format
      • Tile format with no LoD (load).
      • Tile format with LoD (load).
      • Tile format with no LoD (save).
      • Tile format with LoD (save).
    • Custom attributes
  • OpenEXR v2 image
    • Multipart format
      • Load multi-part image
      • Save multi-part image
      • Load multi-part deep image
      • Save multi-part deep image
  • OpenEXR v2 deep image
    • Loading scanline + ZIPS + HALF or FLOAT pixel type.
  • Compression
    • NONE
    • RLE
    • ZIP
    • ZIPS
    • PIZ
    • ZFP (tinyexr extension)
    • B44?
    • B44A?
    • PIX24?
    • DWA (not planned, patent encumbered)
  • Line order.
    • Increasing, decreasing (load)
    • Random?
    • Increasing (save)
    • decreasing (save)
  • Pixel format (UINT, FLOAT).
    • UINT, FLOAT (load)
    • UINT, FLOAT (deep load)
    • UINT, FLOAT (save)
    • UINT, FLOAT (deep save)
  • Support for big endian machine.
    • Loading scanline image
    • Saving scanline image
    • Loading multi-part channel EXR (not tested)
    • Saving multi-part channel EXR (not tested)
    • Loading deep image
    • Saving deep image
  • Optimization
    • C++11 thread loading
    • C++11 thread saving
    • ISPC?
    • OpenMP multi-threading in EXR loading.
    • OpenMP multi-threading in EXR saving.
    • OpenMP multi-threading in deep image loading.
    • OpenMP multi-threading in deep image saving.
  • C interface.
    • You can easily write language bindings (e.g. golang)

Supported platform

  • x86-64
    • Windows 7 or later
    • Linux(posix) system
    • macOS
  • AARCH64
    • aarch64 linux(e.g. Raspberry Pi)
    • Android
    • iOS
    • macOS
  • RISC-V(Should work)
  • Big endian machine(not maintained, but should work)
    • SPARC, PowerPC, ...
  • WebAssembly(JavaScript)
    • Loader only(See js)
  • Python binding

Requirements

  • C++ compiler(C++11 recommended. C++03 may work)

Use case

New TinyEXR (v0.9.5+)

Older TinyEXR (v0.9.0)

Examples

Experimental

Usage

NOTE: API is still subject to change. See the source code for details.

Include tinyexr.h with TINYEXR_IMPLEMENTATION flag (do this only for one .cc file).

//Please include your own zlib-compatible API header before
//including `tinyexr.h` when you disable `TINYEXR_USE_MINIZ`
//#define TINYEXR_USE_MINIZ 0
//#include "zlib.h"
//Or, if your project uses `stb_image[_write].h`, use their
//zlib implementation:
//#define TINYEXR_USE_STB_ZLIB 1
#define TINYEXR_IMPLEMENTATION
#include "tinyexr.h"

Compile flags

  • TINYEXR_USE_MINIZ Use miniz (default = 1). Please include zlib.h header before tinyexr.h if you disable miniz support(e.g. use system's zlib).
  • TINYEXR_USE_STB_ZLIB Use zlib from stb_image[_write].h instead of miniz or the system's zlib (default = 0).
  • TINYEXR_USE_PIZ Enable PIZ compression support (default = 1)
  • TINYEXR_USE_ZFP Enable ZFP compression supoort (TinyEXR extension, default = 0)
  • TINYEXR_USE_THREAD Enable threaded loading using C++11 thread (Requires C++11 compiler, default = 0)
  • TINYEXR_USE_OPENMP Enable OpenMP threading support (default = 1 if _OPENMP is defined)
    • Use TINYEXR_USE_OPENMP=0 to force disable OpenMP code path even if OpenMP is available/enabled in the compiler.

Quickly reading RGB(A) EXR file.

  const char* input = "asakusa.exr";
  float* out; // width * height * RGBA
  int width;
  int height;
  const char* err = NULL; // or nullptr in C++11

  int ret = LoadEXR(&out, &width, &height, input, &err);

  if (ret != TINYEXR_SUCCESS) {
    if (err) {
       fprintf(stderr, "ERR : %s\n", err);
       FreeEXRErrorMessage(err); // release memory of error message.
    }
  } else {
    ...
    free(out); // release memory of image data
  }

Reading layered RGB(A) EXR file.

If you want to read EXR image with layer info (channel has a name with delimiter .), please use LoadEXRWithLayer API.

You need to know layer name in advance (e.g. through EXRLayers API).

  const char* input = ...;
  const char* layer_name = "diffuse"; // or use EXRLayers to get list of layer names in .exr
  float* out; // width * height * RGBA
  int width;
  int height;
  const char* err = NULL; // or nullptr in C++11

  // will read `diffuse.R`, `diffuse.G`, `diffuse.B`, (`diffuse.A`) channels
  int ret = LoadEXRWithLayer(&out, &width, &height, input, layer_name, &err);

  if (ret != TINYEXR_SUCCESS) {
    if (err) {
       fprintf(stderr, "ERR : %s\n", err);
       FreeEXRErrorMessage(err); // release memory of error message.
    }
  } else {
    ...
    free(out); // release memory of image data
  }

Loading Singlepart EXR from a file.

Scanline and tiled format are supported.

  // 1. Read EXR version.
  EXRVersion exr_version;

  int ret = ParseEXRVersionFromFile(&exr_version, argv[1]);
  if (ret != 0) {
    fprintf(stderr, "Invalid EXR file: %s\n", argv[1]);
    return -1;
  }

  if (exr_version.multipart) {
    // must be multipart flag is false.
    return -1;
  }

  // 2. Read EXR header
  EXRHeader exr_header;
  InitEXRHeader(&exr_header);

  const char* err = NULL; // or `nullptr` in C++11 or later.
  ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, argv[1], &err);
  if (ret != 0) {
    fprintf(stderr, "Parse EXR err: %s\n", err);
    FreeEXRErrorMessage(err); // free's buffer for an error message
    return ret;
  }

  // // Read HALF channel as FLOAT.
  // for (int i = 0; i < exr_header.num_channels; i++) {
  //   if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
  //     exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
  //   }
  // }

  EXRImage exr_image;
  InitEXRImage(&exr_image);

  ret = LoadEXRImageFromFile(&exr_image, &exr_header, argv[1], &err);
  if (ret != 0) {
    fprintf(stderr, "Load EXR err: %s\n", err);
    FreeEXRHeader(&exr_header);
    FreeEXRErrorMessage(err); // free's buffer for an error message
    return ret;
  }

  // 3. Access image data
  // `exr_image.images` will be filled when EXR is scanline format.
  // `exr_image.tiled` will be filled when EXR is tiled format.

  // 4. Free image data
  FreeEXRImage(&exr_image);
  FreeEXRHeader(&exr_header);

Loading Multipart EXR from a file.

Scanline and tiled format are supported.

  // 1. Read EXR version.
  EXRVersion exr_version;

  int ret = ParseEXRVersionFromFile(&exr_version, argv[1]);
  if (ret != 0) {
    fprintf(stderr, "Invalid EXR file: %s\n", argv[1]);
    return -1;
  }

  if (!exr_version.multipart) {
    // must be multipart flag is true.
    return -1;
  }

  // 2. Read EXR headers in the EXR.
  EXRHeader **exr_headers; // list of EXRHeader pointers.
  int num_exr_headers;
  const char *err = NULL; // or nullptr in C++11 or later

  // Memory for EXRHeader is allocated inside of ParseEXRMultipartHeaderFromFile,
  ret = ParseEXRMultipartHeaderFromFile(&exr_headers, &num_exr_headers, &exr_version, argv[1], &err);
  if (ret != 0) {
    fprintf(stderr, "Parse EXR err: %s\n", err);
    FreeEXRErrorMessage(err); // free's buffer for an error message
    return ret;
  }

  printf("num parts = %d\n", num_exr_headers);


  // 3. Load images.

  // Prepare array of EXRImage.
  std::vector<EXRImage> images(num_exr_headers);
  for (int i =0; i < num_exr_headers; i++) {
    InitEXRImage(&images[i]);
  }

  ret = LoadEXRMultipartImageFromFile(&images.at(0), const_cast<const EXRHeader**>(exr_headers), num_exr_headers, argv[1], &err);
  if (ret != 0) {
    fprintf(stderr, "Parse EXR err: %s\n", err);
    FreeEXRErrorMessage(err); // free's buffer for an error message
    return ret;
  }

  printf("Loaded %d part images\n", num_exr_headers);

  // 4. Access image data
  // `exr_image.images` will be filled when EXR is scanline format.
  // `exr_image.tiled` will be filled when EXR is tiled format.

  // 5. Free images
  for (int i =0; i < num_exr_headers; i++) {
    FreeEXRImage(&images.at(i));
  }

  // 6. Free headers.
  for (int i =0; i < num_exr_headers; i++) {
    FreeEXRHeader(exr_headers[i]);
    free(exr_headers[i]);
  }
  free(exr_headers);

Saving Scanline EXR file.

  // See `examples/rgbe2exr/` for more details.
  bool SaveEXR(const float* rgb, int width, int height, const char* outfilename) {

    EXRHeader header;
    InitEXRHeader(&header);

    EXRImage image;
    InitEXRImage(&image);

    image.num_channels = 3;

    std::vector<float> images[3];
    images[0].resize(width * height);
    images[1].resize(width * height);
    images[2].resize(width * height);

    // Split RGBRGBRGB... into R, G and B layer
    for (int i = 0; i < width * height; i++) {
      images[0][i] = rgb[3*i+0];
      images[1][i] = rgb[3*i+1];
      images[2][i] = rgb[3*i+2];
    }

    float* image_ptr[3];
    image_ptr[0] = &(images[2].at(0)); // B
    image_ptr[1] = &(images[1].at(0)); // G
    image_ptr[2] = &(images[0].at(0)); // R

    image.images = (unsigned char**)image_ptr;
    image.width = width;
    image.height = height;

    header.num_channels = 3;
    header.channels = (EXRChannelInfo *)malloc(sizeof(EXRChannelInfo) * header.num_channels);
    // Must be (A)BGR order, since most of EXR viewers expect this channel order.
    strncpy(header.channels[0].name, "B", 255); header.channels[0].name[strlen("B")] = '\0';
    strncpy(header.channels[1].name, "G", 255); header.channels[1].name[strlen("G")] = '\0';
    strncpy(header.channels[2].name, "R", 255); header.channels[2].name[strlen("R")] = '\0';

    header.pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
    header.requested_pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
    for (int i = 0; i < header.num_channels; i++) {
      header.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image
      header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_HALF; // pixel type of output image to be stored in .EXR
    }

    const char* err = NULL; // or nullptr in C++11 or later.
    int ret = SaveEXRImageToFile(&image, &header, outfilename, &err);
    if (ret != TINYEXR_SUCCESS) {
      fprintf(stderr, "Save EXR err: %s\n", err);
      FreeEXRErrorMessage(err); // free's buffer for an error message
      return ret;
    }
    printf("Saved exr file. [ %s ] \n", outfilename);

    free(rgb);

    free(header.channels);
    free(header.pixel_types);
    free(header.requested_pixel_types);

  }

Reading deep image EXR file. See example/deepview for actual usage.

  const char* input = "deepimage.exr";
  const char* err = NULL; // or nullptr
  DeepImage deepImage;

  int ret = LoadDeepEXR(&deepImage, input, &err);

  // access to each sample in the deep pixel.
  for (int y = 0; y < deepImage.height; y++) {
    int sampleNum = deepImage.offset_table[y][deepImage.width-1];
    for (int x = 0; x < deepImage.width-1; x++) {
      int s_start = deepImage.offset_table[y][x];
      int s_end   = deepImage.offset_table[y][x+1];
      if (s_start >= sampleNum) {
        continue;
      }
      s_end = (s_end < sampleNum) ? s_end : sampleNum;
      for (int s = s_start; s < s_end; s++) {
        float val = deepImage.image[depthChan][y][s];
        ...
      }
    }
  }

deepview

examples/deepview is simple deep image viewer in OpenGL.

DeepViewExample

TinyEXR extension

ZFP

NOTE

TinyEXR adds ZFP compression as an experimemtal support (Linux and MacOSX only).

ZFP only supports FLOAT format pixel, and its image width and height must be the multiple of 4, since ZFP compresses pixels with 4x4 pixel block.

Setup

Checkout zfp repo as an submodule.

$ git submodule update --init

Build

Then build ZFP

$ cd deps/ZFP
$ mkdir -p lib   # Create `lib` directory if not exist
$ make

Set 1 to TINYEXT_USE_ZFP define in tinyexr.h

Build your app with linking deps/ZFP/lib/libzfp.a

ZFP attribute

For ZFP EXR image, the following attribute must exist in its EXR image.

  • zfpCompressionType (uchar).
    • 0 = fixed rate compression
    • 1 = precision based variable rate compression
    • 2 = accuracy based variable rate compression

And the one of following attributes must exist in EXR, depending on the zfpCompressionType value.

  • zfpCompressionRate (double)
    • Specifies compression rate for fixed rate compression.
  • zfpCompressionPrecision (int32)
    • Specifies the number of bits for precision based variable rate compression.
  • zfpCompressionTolerance (double)
    • Specifies the tolerance value for accuracy based variable rate compression.

Note on ZFP compression.

At least ZFP code itself works well on big endian machine.

Unit tests

See test/unit directory.

TODO

Contribution is welcome!

  • Compression
    • B44?
    • B44A?
    • PIX24?
  • Custom attributes
    • Normal image (EXR 1.x)
    • Deep image (EXR 2.x)
  • JavaScript library (experimental, using Emscripten)
    • LoadEXRFromMemory
    • SaveMultiChannelEXR
    • Deep image save/load
  • Write from/to memory buffer.
    • Deep image save/load
  • Tile format.
    • Tile format with no LoD (load).
    • Tile format with LoD (load).
    • Tile format with no LoD (save).
    • Tile format with LoD (save).
  • Support for custom compression type.
    • zfp compression (Not in OpenEXR spec, though)
    • zstd?
  • Multi-channel.
  • Multi-part (EXR2.0)
    • Load multi-part image
    • Load multi-part deep image
  • Line order.
    • Increasing, decreasing (load)
    • Random?
    • Increasing, decreasing (save)
  • Pixel format (UINT, FLOAT).
    • UINT, FLOAT (load)
    • UINT, FLOAT (deep load)
    • UINT, FLOAT (save)
    • UINT, FLOAT (deep save)
  • Support for big endian machine.
    • Loading multi-part channel EXR
    • Saving multi-part channel EXR
    • Loading deep image
    • Saving deep image
  • Optimization
    • ISPC?
    • OpenMP multi-threading in EXR loading.
    • OpenMP multi-threading in EXR saving.
    • OpenMP multi-threading in deep image loading.
    • OpenMP multi-threading in deep image saving.

Python bindings

pytinyexr is available: https://pypi.org/project/pytinyexr/ (loading only as of 0.9.1)

Similar or related projects

License

3-clause BSD

tinyexr uses miniz, which is developed by Rich Geldreich [email protected], and licensed under public domain.

tinyexr tools uses stb, which is licensed under public domain: https://github.com/nothings/stb tinyexr uses some code from OpenEXR, which is licensed under 3-clause BSD license. tinyexr uses nanozlib and wuffs, whose are licensed unnder Apache 2.0 license.

Author(s)

Syoyo Fujita ([email protected])

Contributor(s)

tinyexr's People

Contributors

adrianatgoogle avatar akien-mga avatar ashpil avatar australiantaxationoffice avatar baldurk avatar bkaradzic avatar blockos avatar danmchan avatar germanaizek avatar inuniku avatar lgtm-migrator avatar marlam avatar mattsarett avatar mike-leo-smith avatar mmp avatar mwkm avatar nbickford-nv avatar nyorain avatar osyotr avatar pjessesco avatar richardeakin avatar roehling avatar selfshadow avatar smerzbach avatar soma-arc avatar ssinai1 avatar syoyo avatar tom7 avatar usakhelo avatar velkyel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tinyexr's Issues

Heap overflow in tinyexr::ReadChannelInfo()

Hello TinyEXR team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
TinyEXR (tested with revision master 94d76d2).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:

  • mkdir project
  • cp Dockerfile /path/to/project
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/poc-heap
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/poc-heap

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000eff6 at pc 0x00000053b0bb bp 0x7ffde32ec970 sp 0x7ffde32ec968
READ of size 4 at 0x60200000eff6 thread T0
#0 0x53b0ba in tinyexr::ReadChannelInfo(std::vector<tinyexr::ChannelInfo, std::allocatortinyexr::ChannelInfo >&, std::vector<unsigned char, std::allocator > const&) /fuzzing/tinyexr/./tinyexr.h:7269:5

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team

alpha of zero returned for RGB-only images

If exrcat from https://gist.github.com/mmp/ba384e1f509e2e38d5df is used on an RGB EXR file, one can see that OpenEXR returns a '1' as a default for the alpha channel while tinyexr returns 0 as a default.

(My suggestion, for what it's worth, would be that tinyexr only return the channels that are present in the file and instead in this case return an array of 3 floats per pixel, returning just the RGB values (and also return to the caller the fact that there were 3 channels in the file, rather than always returning 4 channels.)

thread safety?

Thanks for making this! Love the single header approach.

I am trying to save RGBD data from opencv mats to file, using SaveEXR. It works fine, unless I call my function in multiple threads, in which case it crashes with "Illegal instruction: 4". I am running it on OS X. I have a little wrapper around SaveEXR, listed below-

void writeEXR4ChannelRGBD(
    const Mat& imageBGR,
    const Mat& depthmap,
    const string& filename,
    const float gamma,
    const float unitScale) {

  // scale depthmap to same size as image (it might be smaller)
  Mat resizeDepth;
  resize(
    depthmap,
    resizeDepth,
    imageBGR.size(),
    0, 0,
    CV_INTER_LINEAR);

  // pack RGB and D together. apply gamma correction to RGB, and unit conversion
  // to D.
  Mat rgbd;
  cvtColor(imageBGR, rgbd, CV_BGR2RGBA);
  for (int y = 0; y < rgbd.rows; ++y) {
    for (int x = 0; x < rgbd.cols; ++x) {
      rgbd.at<Vec4f>(y, x)[0] = pow(rgbd.at<Vec4f>(y, x)[0], gamma);
      rgbd.at<Vec4f>(y, x)[1] = pow(rgbd.at<Vec4f>(y, x)[1], gamma);
      rgbd.at<Vec4f>(y, x)[2] = pow(rgbd.at<Vec4f>(y, x)[2], gamma);
      rgbd.at<Vec4f>(y, x)[3] = resizeDepth.at<float>(y, x) * unitScale;
    }
  }

  static const int kChannels = 4;
  if (0 != SaveEXR(
      (const float*)rgbd.data,
      rgbd.cols,
      rgbd.rows,
      kChannels,
      filename.c_str())) {
    throw VrCamException("failed to write EXR: " + filename);
  }
}

About tinyexr.js

Hi,

I discovered your project to have a tinier.js, my question are really basics, is it working and do you have any example that you could share ?

Many thanks

Stephane

TinyEXR corrupts tiny .exr

As I was constructing a simple example for my Haskell library I found that if compressed stream is longer then uncompressed one, the latter has to be stored. In fact, I found a reference to it in spec (http://www.openexr.com/openexrfilelayout.pdf):

Otherwise, the uncompressed pixels are fed to the appropriate compressor, and either the
compressed or the uncompressed data are stored in the file, whichever is smaller.

The problem can be easily fixed by checking the length of the stream and writing the shorter one (the header still keeps the compression flag).

Here is tweaked example that writes single red pixel, but when viewed in GIMP, it is blue (with ZIPS GIMP reports corrupted file):

#include <stdio.h>
#include <stdlib.h>


#define TINYEXR_IMPLEMENTATION
#include "tinyexr.h"


int
main(void)
{
        EXRHeader header;
        InitEXRHeader(&header);

        header.compression_type = TINYEXR_COMPRESSIONTYPE_ZIP;

        EXRImage image;
        InitEXRImage(&image);

        image.num_channels = 3;

        float const images[3][1] = {
                {1.0f},
                {0.0f},
                {0.0f},
        };

        float const *const image_ptr[3] = {
                images[2],
                images[1],
                images[0],
        };

        image.images = (unsigned char **)image_ptr;
        image.width = 1;
        image.height = 1;

        header.num_channels = 3;
        header.channels = (EXRChannelInfo *)malloc(sizeof(EXRChannelInfo) * header.num_channels); 
        // Must be BGR(A) order, since most of EXR viewers expect this channel order.
        strncpy(header.channels[0].name, "B", 255); header.channels[0].name[strlen("B")] = '\0';
        strncpy(header.channels[1].name, "G", 255); header.channels[1].name[strlen("G")] = '\0';
        strncpy(header.channels[2].name, "R", 255); header.channels[2].name[strlen("R")] = '\0';

        header.pixel_types = (int *)malloc(sizeof(int) * header.num_channels); 
        header.requested_pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
        for (int i = 0; i < header.num_channels; i++) {
                header.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image
                header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of output image to be stored in .EXR
        }

        const char* err;
        int const ret = SaveEXRImageToFile(&image, &header, "test.exr", &err);
        if (ret != TINYEXR_SUCCESS) {
                fprintf(stderr, "Save EXR err: %s\n", err);
                return ret;
        }
}

TinyEXR JavaScript version in a browser

I've been trying to run JavaScript version provided in this repo in a browser, but I'm stuck on the step of converting image blob to a nodejs like Buffer. Please suggest

Here is my code:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>TinyEXR Js</title>
  <script src="tinyexr_new.js"></script>
</head>
<body>
</body>
<script>

  var imgData;
  fetch("asakusa.exr")
  .then(function (response) {
    return response.blob();
  })
  .then(function (blob) {

    // TODO(Kirill):
    // Need to convert image blob to a nodejs like Buffer

    // here the image is a blob
    var data = new Uint8Array(buffer);
    console.log('Data length:', data.length);

    ParseEXRHeaderFromMemory = cwrap(
      'ParseEXRHeaderFromMemory', 'number', ['number', 'number', 'number']
    );

    LoadEXRFromMemory = cwrap(
      'LoadEXRFromMemory', 'number', ['number', 'number', 'string']
    );

    var widthPtr = _malloc(4);
    var widthHeap = new Uint8Array(HEAPU8.buffer, widthPtr, 4);
    var heightPtr = _malloc(4);
    var heightHeap = new Uint8Array(HEAPU8.buffer, heightPtr, 4);
    var ptr = _malloc(data.length)
    var dataHeap = new Uint8Array(HEAPU8.buffer, ptr, data.length);
    dataHeap.set(new Uint8Array(data.buffer))

    var ret  = ParseEXRHeaderFromMemory(widthHeap.byteOffset, heightHeap.byteOffset, dataHeap.byteOffset);
    console.log('Exr header:', ret);

    var width = (new Int32Array(widthHeap.buffer, widthHeap.byteOffset, 1))[0];
    var height = (new Int32Array(heightHeap.buffer, heightHeap.byteOffset, 1))[0];
    console.log('Width and height: ', width, height)

    var imgDataLen = width * height * 4 * 4;
    var img = _malloc(imgDataLen)
    var imgHeap = new Float32Array(HEAPU8.buffer, img, imgDataLen/4);
    ret = LoadEXRFromMemory(imgHeap.byteOffset, dataHeap.byteOffset, null)

    // Now imgHeap contains HDR image: float x RGBA x width x height
    console.log(imgHeap[0])
  });



</script>
</html>

Assert failure (71727922)

Hello tinyexr team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
tinyexr (tested with revision * master 4ae1852).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:
artifacts_71727922.zip

  • mkdir project
  • cp Dockerfile.tinyexr /path/to/project/Dockerfile
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/reproducer
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

fuzzer: ./tinyexr.h:7645: void tinyexr::DecompressRle(unsigned char , const unsigned long, const unsigned char , unsigned long): Assertion `ret == static_cast(uncompressed_size)' failed.
==11== ERROR: libFuzzer: deadly signal
#0 0x4db8e3 in __sanitizer_print_stack_trace (/fuzzing/tinyexr/fuzzer+0x4db8e3)
#1 0x50e82a in fuzzer::Fuzzer::CrashCallback() (/fuzzing/tinyexr/fuzzer+0x50e82a)
#2 0x50e7fa in fuzzer::Fuzzer::StaticCrashSignalCallback() (/fuzzing/tinyexr/fuzzer+0x50e7fa)
#3 0x7f6796b050bf (/lib/x86_64-linux-gnu/libpthread.so.0+0x110bf)
#4 0x7f6796164fce in gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x32fce)
#5 0x7f67961663f9 in abort (/lib/x86_64-linux-gnu/libc.so.6+0x343f9)
#6 0x7f679615de36 (/lib/x86_64-linux-gnu/libc.so.6+0x2be36)
#7 0x7f679615dee1 in __assert_fail (/lib/x86_64-linux-gnu/libc.so.6+0x2bee1)
#8 0x549cd1 in tinyexr::DecompressRle(unsigned char
, unsigned long, unsigned char const
, unsigned long) /fuzzing/tinyexr/./tinyexr.h:7645:3
#9 0x54833b in tinyexr::DecodePixelData(unsigned char**, int const*, unsigned char const*, unsigned long, int, int, int, int, int, int, int, int, unsigned long, unsigned long, _EXRAttribute const*, unsigned long, _EXRChannelInfo const*, std::vector<unsigned long, std::allocator > const&) /fuzzing/tinyexr/./tinyexr.h:9670:5
#10 0x53ceed in tinyexr::DecodeChunk(_EXRImage*, _EXRHeader const*, std::vector<unsigned long, std::allocator > const&, unsigned char const*) /fuzzing/tinyexr/./tinyexr.h:10539:16
#11 0x532baf in tinyexr::DecodeEXRImage(_EXRImage*, _EXRHeader const*, unsigned char const*, unsigned char const*, unsigned long, char const**) /fuzzing/tinyexr/./tinyexr.h:10712:10
#12 0x53e827 in LLVMFuzzerTestOneInput /fuzzing/tinyexr/fuzzer.cc:20:9
#13 0x5100dc in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x5100dc)
#14 0x50f89e in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x50f89e)
#15 0x5096fd in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*) (/fuzzing/tinyexr/fuzzer+0x5096fd)
#16 0x50abcf in fuzzer::FuzzerDriver(int*, char***, int ()(unsigned char const, unsigned long)) (/fuzzing/tinyexr/fuzzer+0x50abcf)
#17 0x5095ac in main (/fuzzing/tinyexr/fuzzer+0x5095ac)
#18 0x7f67961522b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
#19 0x41ecb9 in _start (/fuzzing/tinyexr/fuzzer+0x41ecb9)

NOTE: libFuzzer has rudimentary signal handlers.
Combine libFuzzer with AddressSanitizer or similar for better crash reports.
SUMMARY: libFuzzer: deadly signal

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team

Assertion failure - 38057817

Hello tinyexr team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
tinyexr (tested with revision * master cda04e4).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:

  • mkdir project
  • cp Dockerfile /path/to/project
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/reproducer
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

REPRO_START:* master cda04e4
INFO: Seed: 3540412425
/fuzzing/tinyexr/fuzzer: Running 1 inputs 1 time(s) each.
Running: /tmp/poc
fuzzer: ./tinyexr.h:7391: void tinyexr::DecompressZip(unsigned char , unsigned long , const unsigned char , unsigned long): Assertion `ret == miniz::MZ_OK' failed.
==9== ERROR: libFuzzer: deadly signal
#0 0x4db8e3 in __sanitizer_print_stack_trace (/fuzzing/tinyexr/fuzzer+0x4db8e3)
#1 0x50e82a in fuzzer::Fuzzer::CrashCallback() (/fuzzing/tinyexr/fuzzer+0x50e82a)
#2 0x50e7fa in fuzzer::Fuzzer::StaticCrashSignalCallback() (/fuzzing/tinyexr/fuzzer+0x50e7fa)
#3 0x7f57dbe3e0bf (/lib/x86_64-linux-gnu/libpthread.so.0+0x110bf)
#4 0x7f57db49dfce in gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x32fce)
#5 0x7f57db49f3f9 in abort (/lib/x86_64-linux-gnu/libc.so.6+0x343f9)
#6 0x7f57db496e36 (/lib/x86_64-linux-gnu/libc.so.6+0x2be36)
#7 0x7f57db496ee1 in __assert_fail (/lib/x86_64-linux-gnu/libc.so.6+0x2bee1)
#8 0x53b325 in tinyexr::DecompressZip(unsigned char
, unsigned long
, unsigned char const
, unsigned long) /fuzzing/tinyexr/./tinyexr.h:7391:3
#9 0x5470f4 in tinyexr::DecodePixelData(unsigned char**, int const*, unsigned char const*, unsigned long, int, int, int, int, int, int, int, int, unsigned long, unsigned long, _EXRAttribute const*, unsigned long, _EXRChannelInfo const*, std::vector<unsigned long, std::allocator > const&) /fuzzing/tinyexr/./tinyexr.h:9535:5
#10 0x53cee5 in tinyexr::DecodeChunk(_EXRImage*, _EXRHeader const*, std::vector<unsigned long, std::allocator > const&, unsigned char const*) /fuzzing/tinyexr/./tinyexr.h:10517:9
#11 0x532baf in tinyexr::DecodeEXRImage(_EXRImage*, _EXRHeader const*, unsigned char const*, unsigned char const*, unsigned long, char const**) /fuzzing/tinyexr/./tinyexr.h:10687:10
#12 0x53e837 in LLVMFuzzerTestOneInput /fuzzing/tinyexr/fuzzer.cc:20:9
#13 0x5100dc in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x5100dc)
#14 0x50f89e in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x50f89e)
#15 0x5096fd in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*) (/fuzzing/tinyexr/fuzzer+0x5096fd)
#16 0x50abcf in fuzzer::FuzzerDriver(int*, char***, int ()(unsigned char const, unsigned long)) (/fuzzing/tinyexr/fuzzer+0x50abcf)
#17 0x5095ac in main (/fuzzing/tinyexr/fuzzer+0x5095ac)
#18 0x7f57db48b2b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
#19 0x41ecb9 in _start (/fuzzing/tinyexr/fuzzer+0x41ecb9)

NOTE: libFuzzer has rudimentary signal handlers.
Combine libFuzzer with AddressSanitizer or similar for better crash reports.
SUMMARY: libFuzzer: deadly signal

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team
artifacts_38057817.zip

Abort (72007691)

Hello tinyexr team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
tinyexr (tested with revision * master 5058aac).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:
artifacts_72007691.zip

  • mkdir project
  • cp Dockerfile.tinyexr /path/to/project/Dockerfile
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/reproducer
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

INFO: Seed: 604298247
/fuzzing/tinyexr/fuzzer: Running 1 inputs 1 time(s) each.
Running: poc-ffc13e157ce29f8ffa6e88fb20cb182eb601408a449571eeb6fba33bbc88dbd1
fuzzer: ./tinyexr.h:10511: int tinyexr::DecodeChunk(EXRImage *, const EXRHeader *, const std::vector<tinyexr::tinyexr_uint64> &, const unsigned char *, const size_t): Assertion `tile_coordinates[2] == 0' failed.
==7== ERROR: libFuzzer: deadly signal
    #0 0x4db8e3 in __sanitizer_print_stack_trace (/fuzzing/tinyexr/fuzzer+0x4db8e3)
    #1 0x50e82a in fuzzer::Fuzzer::CrashCallback() (/fuzzing/tinyexr/fuzzer+0x50e82a)
    #2 0x50e7fa in fuzzer::Fuzzer::StaticCrashSignalCallback() (/fuzzing/tinyexr/fuzzer+0x50e7fa)
    #3 0x7f288a73a0bf  (/lib/x86_64-linux-gnu/libpthread.so.0+0x110bf)
    #4 0x7f2889d99fce in gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x32fce)
    #5 0x7f2889d9b3f9 in abort (/lib/x86_64-linux-gnu/libc.so.6+0x343f9)
    #6 0x7f2889d92e36  (/lib/x86_64-linux-gnu/libc.so.6+0x2be36)
    #7 0x7f2889d92ee1 in __assert_fail (/lib/x86_64-linux-gnu/libc.so.6+0x2bee1)
    #8 0x53d5d3 in tinyexr::DecodeChunk(_EXRImage*, _EXRHeader const*, std::vector<unsigned long, std::allocator<unsigned long> > const&, unsigned char const*, unsigned long) /fuzzing/tinyexr/./tinyexr.h:10511:7
    #9 0x532ce3 in tinyexr::DecodeEXRImage(_EXRImage*, _EXRHeader const*, unsigned char const*, unsigned char const*, unsigned long, char const**) /fuzzing/tinyexr/./tinyexr.h:10788:10
    #10 0x53ea57 in LLVMFuzzerTestOneInput /fuzzing/tinyexr/tinyexr_fuzzer.cc:32:9
    #11 0x5100dc in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x5100dc)
    #12 0x50f89e in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x50f89e)
    #13 0x5096fd in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*) (/fuzzing/tinyexr/fuzzer+0x5096fd)
    #14 0x50abcf in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/fuzzing/tinyexr/fuzzer+0x50abcf)
    #15 0x5095ac in main (/fuzzing/tinyexr/fuzzer+0x5095ac)
    #16 0x7f2889d872b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
    #17 0x41ecb9 in _start (/fuzzing/tinyexr/fuzzer+0x41ecb9)

NOTE: libFuzzer has rudimentary signal handlers.
      Combine libFuzzer with AddressSanitizer or similar for better crash reports.
SUMMARY: libFuzzer: deadly signal

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team

Process for reporting security bugs

Hello Tinyexr,

As part of our fuzzing efforts at Google, we are interested in understanding the process for reporting potential security issues to your project in a private manner. Could you please advise us if there is a private tracker for these kinds of bugs, or if you prefer them filed in a publicly visible way?

Thanks!

Memory Leak - 70638693

Hello tinyexr team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
tinyexr (tested with revision * master cda04e4).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:

  • mkdir project
  • cp Dockerfile /path/to/project
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/reproducer
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

REPRO_START:* master cda04e4
INFO: Seed: 4093941770
/fuzzing/tinyexr/fuzzer: Running 1 inputs 1 time(s) each.
Running: /tmp/poc
Executed /tmp/poc in 0 ms

=================================================================
==10==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 1 byte(s) in 1 object(s) allocated from:
#0 0x4ce5d8 in __interceptor_malloc (/fuzzing/tinyexr/fuzzer+0x4ce5d8)
#1 0x53c50c in tinyexr::DecodeChunk(_EXRImage*, _EXRHeader const*, std::vector<unsigned long, std::allocator > const&, unsigned char const*) /fuzzing/tinyexr/./tinyexr.h:10424:9
#2 0x532baf in tinyexr::DecodeEXRImage(_EXRImage*, _EXRHeader const*, unsigned char const*, unsigned char const*, unsigned long, char const**) /fuzzing/tinyexr/./tinyexr.h:10687:10
#3 0x53e837 in LLVMFuzzerTestOneInput /fuzzing/tinyexr/fuzzer.cc:20:9
#4 0x5100dc in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x5100dc)
#5 0x5095ac in main (/fuzzing/tinyexr/fuzzer+0x5095ac)

SUMMARY: AddressSanitizer: 1 byte(s) leaked in 1 allocation(s).

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team
artifacts-70638693.zip

memory corruption with piz decompression

With the EXR file now at http://pharr.org/matt/x.exr, DecompressPiz seems to overwrite a buffer. Running tests/exrcat with that file under valgrind gives:

==35== Invalid write of size 8
==35==    at 0x4C2A6FB: memcpy (mc_replace_strmem.c:838)
==35==    by 0x41713D: (anonymous namespace)::DecompressPiz(unsigned char*, unsigned int&, unsigned char const*, unsigned long, std::vector<(anonymous namespace)::ChannelInfo, std::allocator<(anonymous namespace)::ChannelInfo> > const&, int, int) (in /tinyexr/test/exrcat/a.out)
==35==    by 0x418CEB: LoadMultiChannelEXRFromMemory (in /tinyexr/test/exrcat/a.out)
==35==    by 0x41801D: LoadMultiChannelEXRFromFile (in /tinyexr/test/exrcat/a.out)
==35==    by 0x417312: LoadEXR (in /tinyexr/test/exrcat/a.out)
==35==    by 0x41DC4F: main (in /tinyexr/test/exrcat/a.out)
==35==  Address 0x5b6b190 is 0 bytes after a block of size 76,800 alloc'd
==35==    at 0x4C286E7: operator new(unsigned long) (vg_replace_malloc.c:287)
==35==    by 0x424A69: __gnu_cxx::new_allocator<unsigned char>::allocate(unsigned long, void const*) (in /tinyexr/test/exrcat/a.out)
==35==    by 0x423FEB: __gnu_cxx::__alloc_traits<std::allocator<unsigned char> >::allocate(std::allocator<unsigned char>&, unsigned long) (in /tinyexr/test/exrcat/a.out)
==35==    by 0x422E13: std::_Vector_base<unsigned char, std::allocator<unsigned char> >::_M_allocate(unsigned long) (in /tinyexr/test/exrcat/a.out)
==35==    by 0x42278A: std::_Vector_base<unsigned char, std::allocator<unsigned char> >::_M_create_storage(unsigned long) (in /tinyexr/test/exrcat/a.out)
==35==    by 0x4206E0: std::_Vector_base<unsigned char, std::allocator<unsigned char> >::_Vector_base(unsigned long, std::allocator<unsigned char> const&) (in /tinyexr/test/exrcat/a.out)
==35==    by 0x41F9A1: std::vector<unsigned char, std::allocator<unsigned char> >::vector(unsigned long, unsigned char const&, std::allocator<unsigned char> const&) (in /tinyexr/test/exrcat/a.out)
==35==    by 0x418C63: LoadMultiChannelEXRFromMemory (in /tinyexr/test/exrcat/a.out)
==35==    by 0x41801D: LoadMultiChannelEXRFromFile (in /tinyexr/test/exrcat/a.out)
==35==    by 0x417312: LoadEXR (in /tinyexr/test/exrcat/a.out)
==35==    by 0x41DC4F: main (in /tinyexr/test/exrcat/a.out)

Heap Out-Of-Bounds Memory Access

Hello tinyexr team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
tinyexr (tested with revision * master fde4fb5).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:

  • mkdir project
  • cp Dockerfile /path/to/project
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/reproducer
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

=================================================================
==10==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60600000ef97 at pc 0x000000530d00 bp 0x7fffc7850bd0 sp 0x7fffc7850bc8
READ of size 1 at 0x60600000ef97 thread T0
#0 0x530cff in tinyexr::ReadString(std::__cxx11::basic_string<char, std::char_traits, std::allocator >, char const) /fuzzing/tinyexr/./tinyexr.h:7086:11
#1 0x524f22 in tinyexr::ReadChannelInfo(std::vector<tinyexr::ChannelInfo, std::allocatortinyexr::ChannelInfo >&, std::vector<unsigned char, std::allocator > const&) /fuzzing/tinyexr/./tinyexr.h:7222:9
#2 0x51951b in tinyexr::ParseEXRHeader(tinyexr::HeaderInfo*, bool*, _EXRVersion const*, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, unsigned char const, unsigned long) /fuzzing/tinyexr/./tinyexr.h:10164:7
#3 0x518a9a in ParseEXRHeaderFromMemory /fuzzing/tinyexr/./tinyexr.h:10824:13
#4 0x5180d5 in ParseEXRHeaderFromFile /fuzzing/tinyexr/./tinyexr.h:12081:10
#5 0x516e63 in LoadEXR /fuzzing/tinyexr/./tinyexr.h:10695:15
#6 0x528cdb in main /fuzzing/tinyexr/test_tinyexr.cc:120:13
#7 0x7f576a7b52b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
#8 0x41d459 in _start (/fuzzing/tinyexr/test_tinyexr+0x41d459)

0x60600000ef97 is located 0 bytes to the right of 55-byte region [0x60600000ef60,0x60600000ef97)
allocated by thread T0 here:
#0 0x5049d0 in operator new(unsigned long) (/fuzzing/tinyexr/test_tinyexr+0x5049d0)
#1 0x539e7b in std::vector<unsigned char, std::allocator >::_M_fill_insert(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator > >, unsigned long, unsigned char const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/vector.tcc:491:34
#2 0x539bff in std::vector<unsigned char, std::allocator >::resize(unsigned long, unsigned char) /usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/stl_vector.h:717:4
#3 0x524d6f in tinyexr::ReadAttribute(std::__cxx11::basic_string<char, std::char_traits, std::allocator >, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, std::vector<unsigned char, std::allocator >, unsigned long, char const*, unsigned long) /fuzzing/tinyexr/./tinyexr.h:7130:9
#4 0x5190ae in tinyexr::ParseEXRHeader(tinyexr::HeaderInfo*, bool*, _EXRVersion const*, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, unsigned char const, unsigned long) /fuzzing/tinyexr/./tinyexr.h:10094:10
#5 0x518a9a in ParseEXRHeaderFromMemory /fuzzing/tinyexr/./tinyexr.h:10824:13
#6 0x5180d5 in ParseEXRHeaderFromFile /fuzzing/tinyexr/./tinyexr.h:12081:10
#7 0x516e63 in LoadEXR /fuzzing/tinyexr/./tinyexr.h:10695:15
#8 0x528cdb in main /fuzzing/tinyexr/test_tinyexr.cc:120:13
#9 0x7f576a7b52b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)

SUMMARY: AddressSanitizer: heap-buffer-overflow /fuzzing/tinyexr/./tinyexr.h:7086:11 in tinyexr::ReadString(std::__cxx11::basic_string<char, std::char_traits, std::allocator >, char const)
Shadow bytes around the buggy address:
0x0c0c7fff9da0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0c7fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0c7fff9dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0c7fff9dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0c7fff9de0: fa fa fa fa fa fa fa fa fa fa fa fa 00 00 00 00
=>0x0c0c7fff9df0: 00 00[07]fa fa fa fa fa 00 00 00 00 00 00 00 07
0x0c0c7fff9e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0c7fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0c7fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0c7fff9e30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c0c7fff9e40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==10==ABORTING

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team
artifacts_38056183.zip

Assert failure in tinyexr::DecompressRle

Hello TinyEXR team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
Unicorn (tested with revision master 94d76d2).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:

  • mkdir project
  • cp Dockerfile /path/to/project
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/reproducer
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

fuzzer: ./tinyexr.h:7674: void tinyexr::DecompressRle(unsigned char *, const unsigned long, const unsigned char *, unsigned long): Assertion `ret == static_cast(uncompressed_size)' failed.

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team

tinyexr-2018012904.zip

LineOrder inverted

If I'm not mistaken, the code for handling lineOrder is correct but exactly inverted. Per this comment the code is written with the assumption that lineOrder=1 implies increasing line order. However I believe this should be 0, not 1. I also confirmed this with the file layout spec - sorry can't deep-link that but lineOrder is defined on page 15. If if this is true, I'd be happy to submit a PR that flips it.

handling of pixel_types is inconsistent for loading/saving with half

With the LoadMultiChannelEXR* functions, the data returned in ExrImage::images is in the same format as is stored in ExrImage::pixel_types (e.g. half data is returned in half format, etc.)

However, for the Save* functions, if the caller sets pixel_types to TINYEXR_PIXELTYPE_HALF, the pixels must be provided as float32 values and tinyexr does the conversion.

This seems inconsistent. I'd argue that the most consistent thing would be to always return half data directly if the file was stored in half, maybe also providing half->float conversion functions for convenience.

(This is obviously closely related to issue #11.)

Issue compiling with OpenMP with last change

Hi,

You last change:
13c7cfd

introduces a regression which prevents me from compiling tinyexr projects when using OpenMP (cflag "-fopenmp").

You can repro the issue if you change your Makefile and add "-fopenmp" to CXXFLAGS and recompile.

You should get the following compile error:
tinyexr.h: In function ‘int tinyexr::DecodeChunk(EXRImage*, const EXRHeader*, const std::vector&, const unsigned char*)’:
tinyexr.h:10459:9: error: break statement used with OpenMP for loop
break;

According to the OpenMP doc, you can't use a "break" statement inside a for loop:
<<<
The for-loop must be a structured block, and in addition, its execution must not be terminated by a break statement.

Here's some info regarding this issue:
https://software.intel.com/en-us/forums/intel-threading-building-blocks/topic/304882

Thanks a lot!
olivier

Assert failure (71508881)

Hello tinyexr team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
tinyexr (tested with revision * master 4ae1852).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:

  • mkdir project
  • cp Dockerfile.tinyexr /path/to/project/Dockerfile
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/reproducer
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:
artifacts_71508881.zip

terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check: __n (which is 4) >= this->size() (which is 4)
==11== ERROR: libFuzzer: deadly signal
#0 0x4db8e3 in __sanitizer_print_stack_trace (/fuzzing/tinyexr/fuzzer+0x4db8e3)
#1 0x50e82a in fuzzer::Fuzzer::CrashCallback() (/fuzzing/tinyexr/fuzzer+0x50e82a)
#2 0x50e7fa in fuzzer::Fuzzer::StaticCrashSignalCallback() (/fuzzing/tinyexr/fuzzer+0x50e7fa)
#3 0x7f303c4570bf (/lib/x86_64-linux-gnu/libpthread.so.0+0x110bf)
#4 0x7f303bab6fce in gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x32fce)
#5 0x7f303bab83f9 in abort (/lib/x86_64-linux-gnu/libc.so.6+0x343f9)
#6 0x7f303c9f80ac in __gnu_cxx::__verbose_terminate_handler() (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x910ac)
#7 0x7f303c9f6065 (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x8f065)
#8 0x7f303c9f60b0 in std::terminate() (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x8f0b0)
#9 0x7f303c9f62c8 in __cxa_throw (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x8f2c8)
#10 0x7f303ca1eb84 in std::__throw_out_of_range_fmt(char const*, ...) (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0xb7b84)
#11 0x55329d in std::vector<unsigned char, std::allocator >::_M_range_check(unsigned long) const /usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/stl_vector.h:804:4
#12 0x54c7e1 in std::vector<unsigned char, std::allocator >::at(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/stl_vector.h:825:2
#13 0x52f5fe in tinyexr::ParseEXRHeader(tinyexr::HeaderInfo*, bool*, _EXRVersion const*, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, unsigned char const, unsigned long) /fuzzing/tinyexr/./tinyexr.h:10226:46
#14 0x52e80a in ParseEXRHeaderFromMemory /fuzzing/tinyexr/./tinyexr.h:10876:13
#15 0x53e7f8 in LLVMFuzzerTestOneInput /fuzzing/tinyexr/fuzzer.cc:14:9
#16 0x5100dc in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x5100dc)
#17 0x50f89e in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x50f89e)
#18 0x5096fd in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*) (/fuzzing/tinyexr/fuzzer+0x5096fd)
#19 0x50abcf in fuzzer::FuzzerDriver(int*, char***, int ()(unsigned char const, unsigned long)) (/fuzzing/tinyexr/fuzzer+0x50abcf)
#20 0x5095ac in main (/fuzzing/tinyexr/fuzzer+0x5095ac)
#21 0x7f303baa42b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
#22 0x41ecb9 in _start (/fuzzing/tinyexr/fuzzer+0x41ecb9)

NOTE: libFuzzer has rudimentary signal handlers.
Combine libFuzzer with AddressSanitizer or similar for better crash reports.
SUMMARY: libFuzzer: deadly signal

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team

Assertion failure in tinyexr::DecodeTiledPixelData()

Template for upstream notification:

Hello TinyEXR team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
TinyEXR (tested with revision master 94d76d2).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:

  • mkdir project
  • cp Dockerfile /path/to/project
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/poc-assert
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/poc-assert
Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

fuzzer: ./tinyexr.h:9993: void tinyexr::DecodeTiledPixelData(unsigned char **, int *, int *, const int *, const unsigned char *, size_t, int, int, int, int, int, int, int, int, size_t, size_t, const EXRAttribute *, size_t, const EXRChannelInfo *, const std::vector<size_t> &): Assertion `tile_offset_y * tile_size_y < data_height' failed.

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team

tinyexr-2018012903.zip

Segmentation Fault

Hello tinyexr team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
tinyexr (tested with revision * master fde4fb5).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:

  • mkdir project
  • cp Dockerfile /path/to/project
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/reproducer
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

==10==WARNING: AddressSanitizer failed to allocate 0x7ffffffffe000080 bytes
ASAN:DEADLYSIGNAL

==10==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x0000005419d0 bp 0x7ffdcc5ef380 sp 0x7ffdcc5ef380 T0)
==10==The signal is caused by a WRITE memory access.
==10==Hint: address points to the zero page.
#0 0x5419cf in ZSt10__fill_n_aIPymyEN9__gnu_cxx11__enable_ifIXsr11__is_scalarIT1_EE7__valueET_E6__typeES4_T0_RKS3 /usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/stl_algobase.h:754:11
#1 0x54175f in std::vector<unsigned long long, std::allocator >::_M_fill_initialize(unsigned long, unsigned long long const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/stl_vector.h:1299:4
#2 0x51cce6 in tinyexr::DecodeEXRImage(_EXRImage*, _EXRHeader const*, unsigned char const*, unsigned char const*, unsigned long, char const**) /fuzzing/tinyexr/./tinyexr.h:10620:40
#3 0x51839e in LoadEXRImageFromFile /fuzzing/tinyexr/./tinyexr.h:10991:10
#4 0x516fa4 in LoadEXR /fuzzing/tinyexr/./tinyexr.h:10709:15
#5 0x528cdb in main /fuzzing/tinyexr/test_tinyexr.cc:120:13
#6 0x7f7b86a852b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
#7 0x41d459 in _start (/fuzzing/tinyexr/test_tinyexr+0x41d459)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/stl_algobase.h:754:11 in ZSt10__fill_n_aIPymyEN9__gnu_cxx11__enable_ifIXsr11__is_scalarIT1_EE7__valueET_E6__typeES4_T0_RKS3
==10==ABORTING

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team
artifacts_38372694.zip

Signed Integer Overflow (71888109)

Hello tinyexr team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
tinyexr (tested with revision * master 852e6b2).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:
artifacts_71888109.zip

  • mkdir project
  • cp Dockerfile.tinyexr /path/to/project/Dockerfile
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/reproducer
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

INFO: Seed: 1593296907
/fuzzing/tinyexr/fuzzer: Running 1 inputs 1 time(s) each.
Running: /tmp/poc
tinyexr.h:10653:76: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
SUMMARY: AddressSanitizer: undefined-behavior tinyexr.h:10653:76 in 

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team

scanlines returned in reverse order

tinyexr seems to be returning scanlines in reverse order. See https://gist.github.com/mmp/ba384e1f509e2e38d5df for a test program that loads an EXR file using the OpenEXR library and then again with tinyexr (and it then prints out all of the pixel values). For any EXR file I run it on, I see for example this for the (0,0) pixel:

(0, 0): 0.019608 0.019608 0.019608 1.000000 - 0.054901 0.054901 0.054901 0.000000

and this for the first pixel of the last scanline:

(0, 63): 0.054901 0.054901 0.054901 1.000000 - 0.019608 0.019608 0.019608 0.000000

unused variable warning

Hi Syoyo,

Tiny issue: when compiling with "-Wall" flag, we get this warning:

tinyexr.h:9300:10: warning: unused variable 'ret' [-Wunused-variable]

Could you add this:

(void)ret;

line 9305, after the assert to avoid the warning?

Thanks.
-oliv

warning C4334 on VS2015 with update 2

Hi, when buliding tinyexr (e.g. via test.cc) on VS2015 update 2, you get the following warnings:

1>c:\projects\tinyexr\tinyexr.h(2295): warning C4334: '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?)
1>c:\projects\tinyexr\tinyexr.h(8013): warning C4334: '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?)

They're both shifting 1 into a particular bit, so you could fix the warning by explicitly shifting 1ULL so it's 64-bit in the first place.

Or it could be added to the suppressed warnings with #pragma warning(disable : 4334). The only downside of this option is that the first warning on line 2295 is in the miniz code above the existing warning disables.

Heap Out-Of-Bounds Memory Access

Hello tinyexr team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
tinyexr (tested with revision * master fde4fb5).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:

  • mkdir project
  • cp Dockerfile /path/to/project
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/reproducer
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

=================================================================
==10==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000efd3 at pc 0x00000051a784 bp 0x7ffcf83d0690 sp 0x7ffcf83d0688
READ of size 4 at 0x60200000efd3 thread T0
#0 0x51a783 in tinyexr::ParseEXRHeader(tinyexr::HeaderInfo*, bool*, _EXRVersion const*, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, unsigned char const, unsigned long) /fuzzing/tinyexr/./tinyexr.h:10176:7
#1 0x518a9a in ParseEXRHeaderFromMemory /fuzzing/tinyexr/./tinyexr.h:10824:13
#2 0x5180d5 in ParseEXRHeaderFromFile /fuzzing/tinyexr/./tinyexr.h:12081:10
#3 0x516e63 in LoadEXR /fuzzing/tinyexr/./tinyexr.h:10695:15
#4 0x528cdb in main /fuzzing/tinyexr/test_tinyexr.cc:120:13
#5 0x7fb059a772b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
#6 0x41d459 in _start (/fuzzing/tinyexr/test_tinyexr+0x41d459)

0x60200000efd3 is located 2 bytes to the right of 1-byte region [0x60200000efd0,0x60200000efd1)
allocated by thread T0 here:
#0 0x5049d0 in operator new(unsigned long) (/fuzzing/tinyexr/test_tinyexr+0x5049d0)
#1 0x539e7b in std::vector<unsigned char, std::allocator >::_M_fill_insert(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator > >, unsigned long, unsigned char const&) /usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/vector.tcc:491:34
#2 0x539bff in std::vector<unsigned char, std::allocator >::resize(unsigned long, unsigned char) /usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/stl_vector.h:717:4
#3 0x524d6f in tinyexr::ReadAttribute(std::__cxx11::basic_string<char, std::char_traits, std::allocator >, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, std::vector<unsigned char, std::allocator >, unsigned long, char const*, unsigned long) /fuzzing/tinyexr/./tinyexr.h:7130:9
#4 0x5190ae in tinyexr::ParseEXRHeader(tinyexr::HeaderInfo*, bool*, _EXRVersion const*, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, unsigned char const, unsigned long) /fuzzing/tinyexr/./tinyexr.h:10094:10
#5 0x518a9a in ParseEXRHeaderFromMemory /fuzzing/tinyexr/./tinyexr.h:10824:13
#6 0x5180d5 in ParseEXRHeaderFromFile /fuzzing/tinyexr/./tinyexr.h:12081:10
#7 0x516e63 in LoadEXR /fuzzing/tinyexr/./tinyexr.h:10695:15
#8 0x528cdb in main /fuzzing/tinyexr/test_tinyexr.cc:120:13
#9 0x7fb059a772b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)

SUMMARY: AddressSanitizer: heap-buffer-overflow /fuzzing/tinyexr/./tinyexr.h:10176:7 in tinyexr::ParseEXRHeader(tinyexr::HeaderInfo*, bool*, _EXRVersion const*, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, unsigned char const, unsigned long)
Shadow bytes around the buggy address:
0x0c047fff9da0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9de0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c047fff9df0: fa fa fa fa fa fa fa fa fa fa[01]fa fa fa fd fa
0x0c047fff9e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9e30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9e40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==10==ABORTING

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team
b/38482728
artifacts_38482728.zip

Miniz/zlib

I am already using Miniz in my project; I therefore want to do this to disable the embedded version of Miniz:

#define TINYEXR_IMPLEMENTATION
#define TINYEXR_USE_MINIZ 0
#include "miniz.h"
#include "tinyexr.h"

However, tinyexr.h wants to include zlib.h:

#if TINYEXR_USE_MINIZ
#else
#include "zlib.h"
#endif

My opinion is that it would be preferable to remove this dependency and leave it up to the user to include their zlib-compatible header (either zlib.h or miniz.h, as in my case) before including tinyexr.h.

Assertion `num_lines > 0' failed

Hello tinyexr team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
tinyexr (tested with revision * master fde4fb5).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:

  • mkdir project
  • cp Dockerfile /path/to/project
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/reproducer
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

test_tinyexr: ./tinyexr.h:10486: int tinyexr::DecodeChunk(EXRImage *, const EXRHeader *, const std::vectortinyexr::tinyexr_uint64 &, const unsigned char *): Assertion `num_lines > 0' failed.
/fuzzing/repro.sh: line 3: 10 Aborted (core dumped) /fuzzing/tinyexr/test_tinyexr $1

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team

Dockerfile.tinyexr.tar.gz
poc-360c3b0555cb979ca108f2d178cf8a80959cfeabaa4ec1d310d062fa653a8c6b_min.tar.gz

Internal bug id: 70694709

Heap Out-Of-Bounds Memory Access (71722583)

Hello tinyexr team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
tinyexr (tested with revision * master 4ae1852).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:
artifacts_71722583.zip

  • mkdir project
  • cp Dockerfile.tinyexr /path/to/project/Dockerfile
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/reproducer
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

=================================================================
==11==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61700000f826 at pc 0x0000004b7525 bp 0x7ffe908f1a90 sp 0x7ffe908f1240
READ of size 69 at 0x61700000f826 thread T0
#0 0x4b7524 in __asan_memcpy (/fuzzing/tinyexr/fuzzer+0x4b7524)
#1 0x54c5e9 in tinyexr::rleUncompress(int, int, signed char const*, char*) /fuzzing/tinyexr/./tinyexr.h:7538:7
#2 0x549ad3 in tinyexr::DecompressRle(unsigned char*, unsigned long, unsigned char const*, unsigned long) /fuzzing/tinyexr/./tinyexr.h:7641:13
#3 0x54833b in tinyexr::DecodePixelData(unsigned char**, int const*, unsigned char const*, unsigned long, int, int, int, int, int, int, int, int, unsigned long, unsigned long, _EXRAttribute const*, unsigned long, _EXRChannelInfo const*, std::vector<unsigned long, std::allocator > const&) /fuzzing/tinyexr/./tinyexr.h:9670:5
#4 0x53ceed in tinyexr::DecodeChunk(_EXRImage*, _EXRHeader const*, std::vector<unsigned long, std::allocator > const&, unsigned char const*) /fuzzing/tinyexr/./tinyexr.h:10539:16
#5 0x532baf in tinyexr::DecodeEXRImage(_EXRImage*, _EXRHeader const*, unsigned char const*, unsigned char const*, unsigned long, char const**) /fuzzing/tinyexr/./tinyexr.h:10712:10
#6 0x53e827 in LLVMFuzzerTestOneInput /fuzzing/tinyexr/fuzzer.cc:20:9
#7 0x5100dc in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x5100dc)
#8 0x50f89e in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x50f89e)
#9 0x5096fd in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*) (/fuzzing/tinyexr/fuzzer+0x5096fd)
#10 0x50abcf in fuzzer::FuzzerDriver(int*, char***, int ()(unsigned char const, unsigned long)) (/fuzzing/tinyexr/fuzzer+0x50abcf)
#11 0x5095ac in main (/fuzzing/tinyexr/fuzzer+0x5095ac)
#12 0x7f85601d22b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
#13 0x41ecb9 in _start (/fuzzing/tinyexr/fuzzer+0x41ecb9)

0x61700000f826 is located 0 bytes to the right of 678-byte region [0x61700000f580,0x61700000f826)
allocated by thread T0 here:
#0 0x5063d0 in operator new[](unsigned long) (/fuzzing/tinyexr/fuzzer+0x5063d0)
#1 0x51007d in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x51007d)
#2 0x5095ac in main (/fuzzing/tinyexr/fuzzer+0x5095ac)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/fuzzing/tinyexr/fuzzer+0x4b7524) in __asan_memcpy
Shadow bytes around the buggy address:
0x0c2e7fff9eb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2e7fff9ec0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2e7fff9ed0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2e7fff9ee0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2e7fff9ef0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c2e7fff9f00: 00 00 00 00[06]fa fa fa fa fa fa fa fa fa fa fa
0x0c2e7fff9f10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c2e7fff9f20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2e7fff9f30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2e7fff9f40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c2e7fff9f50: 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
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==11==ABORTING

Please find a proposed patch in 0001-Fix-a-heap-OOB-read-in-DecodeChunk.zip

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team

Hardening of decompression API and implementation

As far as I can tell, the tineyexr decompression APIs (ex: ParseEXRHeaderFromMemory, LoadEXRImageFromMemory, LoadEXRFromMemory) are very trusting of the input they receive.

In particular, they take a pointer to memory and assume that all of necessary memory is there (please correct me if I'm wrong).

This isn't a good fit for a decoders that might be decoding untrusted user data etc. It would be possible for an attacker to cause us to read off the end of memory.

Would tinyexr be receptive to patches that improve this aspect of the decode API implementation? I'd like to think that the changes might be simple.

Ex: Pass in a size parameter in addition to the memory ptr and add various checks throughout the decode.

if (size < numberBytesWeNeedToRead) {
    return ERROR;
}

PIZ compression support ?

I have integrated your library which seems to be great, and very easy to integrate.
No luck, all the .exr files sent by our artist are using the PIZ compression scheme, and therefore not supported by the loader :(
Is there any plan to add support for this compression scheme ?

Thanks a lot for your great work BTW !

Segmentation Fault at tinyexr::DecodePixelData

Hello tinyexr team,

As part of our fuzzing efforts at Google, we have identified an issue affecting tinyexr (tested with revision 0da3c06). To reproduce we are attaching a dockerfile to help compiling the project with the LLVM taking advantage of the sanitizers that it offers.

Attached is a dockerfile that can be used for reproduction. More information about how to use the dockerfile can be found here: https://docs.docker.com/engine/reference/builder/
TL;DR instructions:
mkdir
cp Dockerfile /path/to/
docker build --no-cache /path/to/
docker run -it
(from another terminal, outside the container):
docker cp /path/to/attached/reproducer :/fuzzing/reproducer
https://docs.docker.com/engine/reference/commandline/cp/
(back inside the container) /fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other instrumentation tools to aid in the investigation.

Additional information:

DecodeChunk() reads line numbers from the input, but does not sanity check them / validate. In this case, the line_no read by DecodeChunk is -144 and later used in DecodePixelData as one of the operands in determining where to write a decoded pixel value, like this:
https://github.com/syoyo/tinyexr/blob/master/tinyexr.h#L9534 (line_no is directly from the input here), allowing an attacker to influence the address and value that is written in the *image = ... lines:

          tinyexr::FP32 f32 = half_to_float(hf);
          float *image = reinterpret_cast<float **>(out_images)[c];
          if (line_order == 0) {
            image += (static_cast<size_t>(line_no) + v) *
                         static_cast<size_t>(x_stride) +
                     u;
          } else {
            image += (static_cast<size_t>(height) - 1U -
                      (static_cast<size_t>(line_no) + v)) *
                         static_cast<size_t>(x_stride) +
                     u;
          }
          *image = f32.f;

Tested Version: git 0da3c06

The sanitizer error that we encountered is here:

ASAN:DEADLYSIGNAL

==21==ERROR: AddressSanitizer: SEGV on unknown address 0x7f9b5a137800 (pc 0x00000052fb39 bp 0x7ffe58f39180 sp 0x7ffe58f38f20 T0)
==21==The signal is caused by a WRITE memory access.
#0 0x52fb38 in tinyexr::DecodePixelData(unsigned char**, int const*, unsigned char const*, unsigned long, int, int, int, int, int, int, int, int, unsigned long, unsigned long, _EXRAttribute const*, unsigned long, _EXRChannelInfo const*, std::vector<unsigned long, std::allocator > const&) /fuzzing/tinyexr/./tinyexr.h:9545:22
#1 0x5262e0 in tinyexr::DecodeChunk(_EXRImage*, _EXRHeader const*, std::vector<unsigned long long, std::allocator > const&, unsigned char const*) /fuzzing/tinyexr/./tinyexr.h:10450:7
#2 0x51c56f in tinyexr::DecodeEXRImage(_EXRImage*, _EXRHeader const*, unsigned char const*, unsigned char const*, unsigned long, char const**) /fuzzing/tinyexr/./tinyexr.h:10608:10
#3 0x517efe in LoadEXRImageFromFile /fuzzing/tinyexr/./tinyexr.h:10919:10
#4 0x516cb2 in LoadEXR /fuzzing/tinyexr/./tinyexr.h:10657:15
#5 0x527b7b in main /fuzzing/tinyexr/test_tinyexr.cc:120:13
#6 0x7f9b5c6dc2b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
#7 0x41d459 in _start (/fuzzing/tinyexr/test_tinyexr+0x41d459)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /fuzzing/tinyexr/./tinyexr.h:9545:22 in tinyexr::DecodePixelData(unsigned char**, int const*, unsigned char const*, unsigned long, int, int, int, int, int, int, int, int, unsigned long, unsigned long, _EXRAttribute const*, unsigned long, _EXRChannelInfo const*, std::vector<unsigned long, std::allocator > const&)
==21==ABORTING
Minimized PoC: Doesn't minimize well / at all, hits too many other failures.
Dockerfile: See attached.

Dockerfile.zip
poc-e7fa6404daa861369d2172fe68e08f9d38c0989f57da7bcfb510bab67e19ca9f.zip

We will gladly work with you so you can successfully confirm and reproduce this issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we’d appreciate to learn your expected timeline for an update to be released. With any fix, please attribute the report to “Google Autofuzz project”.

We are also pleased to inform you that your project is eligible for inclusion to the OSS-Fuzz project, which can provide additional continuous fuzzing, and encourage you to investigate integration options.

Don’t hesitate to let us know if you have any questions!

Google AutoFuzz Team

Loading half data directly

Hi - thanks for all your work on this library. I'm working with @richardeakin to explore integrating it with Cinder. If I'm not misreading it, it looks like there's no way to load image data as half even when that is the format on disk. Is this something you'd be open to adding? Essentially we're trying to do a fp16 .exr -> OpenGL texture pipeline without any intermediate conversion to or from float.

Out of Bounds Read - 65733047

Hello tinyexr team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
tinyexr (tested with revision * master cda04e4).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:

  • mkdir project
  • cp Dockerfile /path/to/project
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/reproducer
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

REPRO_START:* master cda04e4
INFO: Seed: 2735981577
/fuzzing/tinyexr/fuzzer: Running 1 inputs 1 time(s) each.
Running: /tmp/poc
terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check: __n (which is 0) >= this->size() (which is 0)
==9== ERROR: libFuzzer: deadly signal
#0 0x4db8e3 in __sanitizer_print_stack_trace (/fuzzing/tinyexr/fuzzer+0x4db8e3)
#1 0x50e82a in fuzzer::Fuzzer::CrashCallback() (/fuzzing/tinyexr/fuzzer+0x50e82a)
#2 0x50e7fa in fuzzer::Fuzzer::StaticCrashSignalCallback() (/fuzzing/tinyexr/fuzzer+0x50e7fa)
#3 0x7f966b5b00bf (/lib/x86_64-linux-gnu/libpthread.so.0+0x110bf)
#4 0x7f966ac0ffce in gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x32fce)
#5 0x7f966ac113f9 in abort (/lib/x86_64-linux-gnu/libc.so.6+0x343f9)
#6 0x7f966bb510ac in __gnu_cxx::__verbose_terminate_handler() (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x910ac)
#7 0x7f966bb4f065 (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x8f065)
#8 0x7f966bb4f0b0 in std::terminate() (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x8f0b0)
#9 0x7f966bb4f2c8 in __cxa_throw (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0x8f2c8)
#10 0x7f966bb77b84 in std::__throw_out_of_range_fmt(char const*, ...) (/usr/lib/x86_64-linux-gnu/libstdc++.so.6+0xb7b84)
#11 0x5532bd in std::vector<unsigned char, std::allocator >::_M_range_check(unsigned long) const /usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/stl_vector.h:804:4
#12 0x54c801 in std::vector<unsigned char, std::allocator >::at(unsigned long) /usr/bin/../lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/bits/stl_vector.h:825:2
#13 0x53aa1d in tinyexr::ReadAttribute(std::__cxx11::basic_string<char, std::char_traits, std::allocator >, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, std::vector<unsigned char, std::allocator >, unsigned long, char const*, unsigned long) /fuzzing/tinyexr/./tinyexr.h:7137:17
#14 0x52ee28 in tinyexr::ParseEXRHeader(tinyexr::HeaderInfo*, bool*, _EXRVersion const*, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, unsigned char const, unsigned long) /fuzzing/tinyexr/./tinyexr.h:10106:10
#15 0x52e80a in ParseEXRHeaderFromMemory /fuzzing/tinyexr/./tinyexr.h:10851:13
#16 0x53e808 in LLVMFuzzerTestOneInput /fuzzing/tinyexr/fuzzer.cc:14:9
#17 0x5100dc in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x5100dc)
#18 0x50f89e in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x50f89e)
#19 0x5096fd in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*) (/fuzzing/tinyexr/fuzzer+0x5096fd)
#20 0x50abcf in fuzzer::FuzzerDriver(int*, char***, int ()(unsigned char const, unsigned long)) (/fuzzing/tinyexr/fuzzer+0x50abcf)
#21 0x5095ac in main (/fuzzing/tinyexr/fuzzer+0x5095ac)
#22 0x7f966abfd2b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
#23 0x41ecb9 in _start (/fuzzing/tinyexr/fuzzer+0x41ecb9)

NOTE: libFuzzer has rudimentary signal handlers.
Combine libFuzzer with AddressSanitizer or similar for better crash reports.
SUMMARY: libFuzzer: deadly signal

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team
artifacts_65733047.zip

Check if required fields exist in header attributes.

According to the spec http://www.openexr.com/openexrfilelayout.pdf ,

The following attributes must exist in EXR header, but current TinyEXR does not check for it.

Header Attributes (All Files)
The header of every OpenEXR file must contain at least the following attributes:

attribute name attribute type
channels chlist
compression compression
dataWindow box2i
displayWindow box2i
lineOrder lineOrder
pixelAspectRatio float
screenWindowCenter v2f
screenWindowWidth float

memory leak

Hello syoyo-san,

Thanks for providing an awesome IO library,
but a memory leak is detected in my environment (Visual C++ 2015).

The leak is detected inside "LoadEXR()" function.
Probably just missing "FreeEXRImage" function at the end of "LoadEXR()" function.
So, my idea is, just insert:
tinyexr.h:10446 FreeEXRImage(&exr_image);

Thanks!

Heap out-of-bounds read found by fuzzing

Hello TinyEXR team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
TinyEXR (tested with revision master 94d76d2).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:

  • mkdir project
  • cp Dockerfile /path/to/project
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/poc running_container_hostname:/fuzzing/poc
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

==29==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000ee38 at pc 0x000000549114 bp 0x7ffddad13b50 sp 0x7ffddad13b48
READ of size 4 at 0x60200000ee38 thread T0
#0 0x549113 in tinyexr::DecodePixelData(unsigned char**, int const*, unsigned char const*, unsigned long, int, int, int, int, int, int, int, int, unsigned long, unsigned long, _EXRAttribute const*, unsigned long, _EXRChannelInfo const*, std::vector<unsigned long, std::allocator > const&) /fuzzing/tinyexr/./tinyexr.h:9517:32

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team

tinyexr-2018012901.zip

Segmentation Fault on 64 bit

When attempting to load EXR images on a 64 bit computer I get a segmentation fault on this line:

tinyexr.cc:7039
    (*out_rgba)[4 * i + 3] = exrImage.images[idxA][i];

This worked fine on a 32 bit computer so I think that the architecture of the computer might be the problem.

Heap-buffer-overflow in ParseEXRHeader

Use Google-Autofuzz fuzz.cc file to prove

git log

commit 852e6b25b48b9ce5d73d4dd0fb0162386f7f3ced
Author: Syoyo Fujita <[email protected]>
Date:   Thu Jan 11 21:38:40 2018 +0900

    Fix a heap OOB read in DecodeChunk. Fixes #60
==36==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000efb3 at pc 0x00000053069b bp 0x7fff61d178b0 sp 0x7fff61d178a8
READ of size 4 at 0x60200000efb3 thread T0
    #0 0x53069a in tinyexr::ParseEXRHeader(tinyexr::HeaderInfo*, bool*, _EXRVersion const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, unsigned char const*, unsigned long) /fuzzing/tinyexr/./tinyexr.h:10246:7
    #1 0x52e80a in ParseEXRHeaderFromMemory /fuzzing/tinyexr/./tinyexr.h:10898:13
    #2 0x53e8e8 in LLVMFuzzerTestOneInput /fuzzing/tinyexr/fuzzer.cc:14:9
    #3 0x5100dc in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x5100dc)
    #4 0x50f89e in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x50f89e)
    #5 0x5096fd in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*) (/fuzzing/tinyexr/fuzzer+0x5096fd)
    #6 0x50abcf in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/fuzzing/tinyexr/fuzzer+0x50abcf)
    #7 0x5095ac in main (/fuzzing/tinyexr/fuzzer+0x5095ac)
    #8 0x7f4e8fa1b2b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
    #9 0x41ecb9 in _start (/fuzzing/tinyexr/fuzzer+0x41ecb9)

0x60200000efb3 is located 2 bytes to the right of 1-byte region [0x60200000efb0,0x60200000efb1)
allocated by thread T0 here:
    #0 0x506230 in operator new(unsigned long) (/fuzzing/tinyexr/fuzzer+0x506230)
    #1 0x5137e6 in std::vector<unsigned char, std::allocator<unsigned char> >::_M_default_append(unsigned long) (/fuzzing/tinyexr/fuzzer+0x5137e6)
    #2 0x52ee28 in tinyexr::ParseEXRHeader(tinyexr::HeaderInfo*, bool*, _EXRVersion const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, unsigned char const*, unsigned long) /fuzzing/tinyexr/./tinyexr.h:10127:10
    #3 0x52e80a in ParseEXRHeaderFromMemory /fuzzing/tinyexr/./tinyexr.h:10898:13
    #4 0x53e8e8 in LLVMFuzzerTestOneInput /fuzzing/tinyexr/fuzzer.cc:14:9
    #5 0x5100dc in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x5100dc)
    #6 0x5095ac in main (/fuzzing/tinyexr/fuzzer+0x5095ac)

SUMMARY: AddressSanitizer: heap-buffer-overflow /fuzzing/tinyexr/./tinyexr.h:10246:7 in tinyexr::ParseEXRHeader(tinyexr::HeaderInfo*, bool*, _EXRVersion const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, unsigned char const*, unsigned long)
Shadow bytes around the buggy address:
  0x0c047fff9da0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9de0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c047fff9df0: fa fa fa fa fa fa[01]fa fa fa 01 fa fa fa fd fa
  0x0c047fff9e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9e40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==36==ABORTING

testcase:https://github.com/xcainiao/poc/blob/master/tinyexr_memcpy_heap-buffer-overflow

Heap Out-Of-Bounds Memory Access (71565214)

Hello tinyexr team,

As part of our fuzzing efforts at Google, we have identified an issue affecting
tinyexr (tested with revision * master 4ae1852).

To reproduce, we are attaching a Dockerfile which compiles the project with
LLVM, taking advantage of the sanitizers that it offers. More information about
how to use the attached Dockerfile can be found here:
https://docs.docker.com/engine/reference/builder/

TL;DR instructions:
artifacts_71565214.zip

  • mkdir project
  • cp Dockerfile.tinyexr /path/to/project/Dockerfile
  • docker build --no-cache /path/to/project
  • docker run -it image_id_from_docker_build

From another terminal, outside the container:
docker cp /path/to/attached/reproducer running_container_hostname:/fuzzing/reproducer
(reference: https://docs.docker.com/engine/reference/commandline/cp/)

And, back inside the container:
/fuzzing/repro.sh /fuzzing/reproducer

Alternatively, and depending on the bug, you could use gcc, valgrind or other
instrumentation tools to aid in the investigation. The sanitizer error that we
encountered is here:

INFO: Seed: 1593562122
/fuzzing/tinyexr/fuzzer: Running 1 inputs 1 time(s) each.
Running: /tmp/poc
=================================================================
==10==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62100001b679 at pc 0x0000005438fc bp 0x7fff74ecbcc0 sp 0x7fff74ecbcb8
READ of size 4 at 0x62100001b679 thread T0
    #0 0x5438fb in tinyexr::ReconstructLineOffsets(std::vector<unsigned long, std::allocator<unsigned long> >*, unsigned long, unsigned char const*, unsigned char const*, unsigned long) /fuzzing/tinyexr/./tinyexr.h:10593:5
    #1 0x532b96 in tinyexr::DecodeEXRImage(_EXRImage*, _EXRHeader const*, unsigned char const*, unsigned char const*, unsigned long, char const**) /fuzzing/tinyexr/./tinyexr.h:10699:11
    #2 0x53e827 in LLVMFuzzerTestOneInput /fuzzing/tinyexr/fuzzer.cc:20:9
    #3 0x5100dc in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x5100dc)
    #4 0x50f89e in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x50f89e)
    #5 0x5096fd in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*) (/fuzzing/tinyexr/fuzzer+0x5096fd)
    #6 0x50abcf in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/fuzzing/tinyexr/fuzzer+0x50abcf)
    #7 0x5095ac in main (/fuzzing/tinyexr/fuzzer+0x5095ac)
    #8 0x7ffb4b3ad2b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
    #9 0x41ecb9 in _start (/fuzzing/tinyexr/fuzzer+0x41ecb9)

0x62100001b679 is located 2 bytes to the right of 4471-byte region [0x62100001a500,0x62100001b677)
allocated by thread T0 here:
    #0 0x5063d0 in operator new[](unsigned long) (/fuzzing/tinyexr/fuzzer+0x5063d0)
    #1 0x51007d in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzzing/tinyexr/fuzzer+0x51007d)
    #2 0x5095ac in main (/fuzzing/tinyexr/fuzzer+0x5095ac)

SUMMARY: AddressSanitizer: heap-buffer-overflow /fuzzing/tinyexr/./tinyexr.h:10593:5 in tinyexr::ReconstructLineOffsets(std::vector<unsigned long, std::allocator<unsigned long> >*, unsigned long, unsigned char const*, unsigned char const*, unsigned long)
Shadow bytes around the buggy address:
  0x0c427fffb670: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fffb680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fffb690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fffb6a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fffb6b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c427fffb6c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07[fa]
  0x0c427fffb6d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fffb6e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fffb6f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fffb700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fffb710: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==10==ABORTING

Our proposed patch to fix the issue is:

diff --git a/tinyexr.h b/tinyexr.h
index 3f468c8..8c0c1bf 100644
--- a/tinyexr.h
+++ b/tinyexr.h
@@ -10582,7 +10582,7 @@ static bool ReconstructLineOffsets(
   for (size_t i = 0; i < n; i++) {
     size_t offset = static_cast<size_t>(marker - head);
     // Offset should not exceed whole EXR file/data size.
-    if (offset >= size) {
+    if ((offset + sizeof(tinyexr::tinyexr_uint64))>= size) {
       return false;
     }

We will gladly work with you so you can successfully confirm and reproduce this
issue. Do let us know if you have any feedback surrounding the documentation.

Once you have reproduced the issue, we'd appreciate to learn your expected
timeline for an update to be released. With any fix, please attribute the report
to "Google Autofuzz project".

We are also pleased to inform you that your project is eligible for inclusion to
the OSS-Fuzz project, which can provide additional continuous fuzzing, and
encourage you to investigate integration options.

Don't hesitate to let us know if you have any questions!

Google AutoFuzz Team

What is the recommended way to save an .exr file?

Hello, and thanks for making this code available, we are evaluating making use of it within the libcinder project. I was hoping you could clarify what the recommended way is to write an .exr file, as the usage example doesn't specify how to do this. I was looking at exrwritetest.cpp which uses SaveEXR, but this appears to be commented out, so is the recommended approach to use SaveMultiChannelEXRToFile instead?

Thanks for helping me get started!
Rich

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.