tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

dec_jpeg_data.cc (5210B)


      1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style
      4 // license that can be found in the LICENSE file.
      5 
      6 #include "lib/jxl/jpeg/dec_jpeg_data.h"
      7 
      8 #include <brotli/decode.h>
      9 
     10 #include "lib/jxl/base/sanitizers.h"
     11 #include "lib/jxl/base/span.h"
     12 #include "lib/jxl/base/status.h"
     13 #include "lib/jxl/dec_bit_reader.h"
     14 
     15 namespace jxl {
     16 namespace jpeg {
     17 Status DecodeJPEGData(Span<const uint8_t> encoded, JPEGData* jpeg_data) {
     18  Status ret = true;
     19  const uint8_t* in = encoded.data();
     20  size_t available_in = encoded.size();
     21  {
     22    BitReader br(encoded);
     23    BitReaderScopedCloser br_closer(br, ret);
     24    JXL_RETURN_IF_ERROR(Bundle::Read(&br, jpeg_data));
     25    JXL_RETURN_IF_ERROR(br.JumpToByteBoundary());
     26    in += br.TotalBitsConsumed() / 8;
     27    available_in -= br.TotalBitsConsumed() / 8;
     28  }
     29  JXL_RETURN_IF_ERROR(ret);
     30 
     31  BrotliDecoderState* brotli_dec =
     32      BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
     33 
     34  struct BrotliDecDeleter {
     35    BrotliDecoderState* brotli_dec;
     36    ~BrotliDecDeleter() { BrotliDecoderDestroyInstance(brotli_dec); }
     37  } brotli_dec_deleter{brotli_dec};
     38 
     39  BrotliDecoderResult result =
     40      BrotliDecoderResult::BROTLI_DECODER_RESULT_SUCCESS;
     41 
     42  auto br_read = [&](std::vector<uint8_t>& data) -> Status {
     43    size_t available_out = data.size();
     44    uint8_t* out = data.data();
     45    while (available_out != 0) {
     46      if (BrotliDecoderIsFinished(brotli_dec)) {
     47        return JXL_FAILURE("Not enough decompressed output");
     48      }
     49      uint8_t* next_out_before = out;
     50      size_t avail_out_before = available_out;
     51      msan::MemoryIsInitialized(in, available_in);
     52      result = BrotliDecoderDecompressStream(brotli_dec, &available_in, &in,
     53                                             &available_out, &out, nullptr);
     54      if (result !=
     55              BrotliDecoderResult::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT &&
     56          result != BrotliDecoderResult::BROTLI_DECODER_RESULT_SUCCESS) {
     57        return JXL_FAILURE(
     58            "Brotli decoding error: %s\n",
     59            BrotliDecoderErrorString(BrotliDecoderGetErrorCode(brotli_dec)));
     60      }
     61      msan::UnpoisonMemory(next_out_before, avail_out_before - available_out);
     62    }
     63    return true;
     64  };
     65  size_t num_icc = 0;
     66  for (size_t i = 0; i < jpeg_data->app_data.size(); i++) {
     67    auto& marker = jpeg_data->app_data[i];
     68    if (jpeg_data->app_marker_type[i] != AppMarkerType::kUnknown) {
     69      // Set the size of the marker.
     70      size_t size_minus_1 = marker.size() - 1;
     71      marker[1] = size_minus_1 >> 8;
     72      marker[2] = size_minus_1 & 0xFF;
     73      if (jpeg_data->app_marker_type[i] == AppMarkerType::kICC) {
     74        if (marker.size() < 17) {
     75          return JXL_FAILURE("ICC markers must be at least 17 bytes");
     76        }
     77        marker[0] = 0xE2;
     78        memcpy(&marker[3], kIccProfileTag, sizeof kIccProfileTag);
     79        marker[15] = ++num_icc;
     80      }
     81    } else {
     82      JXL_RETURN_IF_ERROR(br_read(marker));
     83      if (marker[1] * 256u + marker[2] + 1u != marker.size()) {
     84        return JXL_FAILURE("Incorrect marker size");
     85      }
     86    }
     87  }
     88  for (size_t i = 0; i < jpeg_data->app_data.size(); i++) {
     89    auto& marker = jpeg_data->app_data[i];
     90    if (jpeg_data->app_marker_type[i] == AppMarkerType::kICC) {
     91      marker[16] = num_icc;
     92    }
     93    if (jpeg_data->app_marker_type[i] == AppMarkerType::kExif) {
     94      marker[0] = 0xE1;
     95      if (marker.size() < 3 + sizeof kExifTag) {
     96        return JXL_FAILURE("Incorrect Exif marker size");
     97      }
     98      memcpy(&marker[3], kExifTag, sizeof kExifTag);
     99    }
    100    if (jpeg_data->app_marker_type[i] == AppMarkerType::kXMP) {
    101      marker[0] = 0xE1;
    102      if (marker.size() < 3 + sizeof kXMPTag) {
    103        return JXL_FAILURE("Incorrect XMP marker size");
    104      }
    105      memcpy(&marker[3], kXMPTag, sizeof kXMPTag);
    106    }
    107  }
    108  // TODO(eustas): actually inject ICC profile and check it fits perfectly.
    109  for (auto& marker : jpeg_data->com_data) {
    110    JXL_RETURN_IF_ERROR(br_read(marker));
    111    if (marker[1] * 256u + marker[2] + 1u != marker.size()) {
    112      return JXL_FAILURE("Incorrect marker size");
    113    }
    114  }
    115  for (auto& data : jpeg_data->inter_marker_data) {
    116    JXL_RETURN_IF_ERROR(br_read(data));
    117  }
    118  JXL_RETURN_IF_ERROR(br_read(jpeg_data->tail_data));
    119 
    120  // Check if there is more decompressed output.
    121  size_t available_out = 1;
    122  uint64_t sink;
    123  uint8_t* next_out = reinterpret_cast<uint8_t*>(&sink);
    124  result = BrotliDecoderDecompressStream(brotli_dec, &available_in, &in,
    125                                         &available_out, &next_out, nullptr);
    126  if (available_out == 0 ||
    127      result == BrotliDecoderResult::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
    128    return JXL_FAILURE("Excess data in compressed stream");
    129  }
    130  if (result == BrotliDecoderResult::BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
    131    return JXL_FAILURE("Incomplete brotli-stream");
    132  }
    133  if (!BrotliDecoderIsFinished(brotli_dec) ||
    134      result != BrotliDecoderResult::BROTLI_DECODER_RESULT_SUCCESS) {
    135    return JXL_FAILURE("Corrupted brotli-stream");
    136  }
    137  if (available_in != 0) {
    138    return JXL_FAILURE("Unused data after brotli stream");
    139  }
    140 
    141  return true;
    142 }
    143 }  // namespace jpeg
    144 }  // namespace jxl