Coverage Report

Created: 2025-06-16 07:00

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