jpg.cc (12294B)
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/extras/dec/jpg.h" 7 8 #if JPEGXL_ENABLE_JPEG 9 #include "lib/jxl/base/include_jpeglib.h" // NOLINT 10 #endif 11 12 #include <algorithm> 13 #include <cstdint> 14 #include <numeric> 15 #include <utility> 16 #include <vector> 17 18 #include "lib/extras/size_constraints.h" 19 #include "lib/jxl/base/compiler_specific.h" 20 #include "lib/jxl/base/sanitizers.h" 21 #include "lib/jxl/base/status.h" 22 23 namespace jxl { 24 namespace extras { 25 26 #if JPEGXL_ENABLE_JPEG 27 namespace { 28 29 constexpr unsigned char kICCSignature[12] = { 30 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00}; 31 constexpr int kICCMarker = JPEG_APP0 + 2; 32 33 constexpr unsigned char kExifSignature[6] = {0x45, 0x78, 0x69, 34 0x66, 0x00, 0x00}; 35 constexpr int kExifMarker = JPEG_APP0 + 1; 36 37 inline bool IsJPG(const Span<const uint8_t> bytes) { 38 if (bytes.size() < 2) return false; 39 if (bytes[0] != 0xFF || bytes[1] != 0xD8) return false; 40 return true; 41 } 42 43 bool MarkerIsICC(const jpeg_saved_marker_ptr marker) { 44 return marker->marker == kICCMarker && 45 marker->data_length >= sizeof kICCSignature + 2 && 46 std::equal(std::begin(kICCSignature), std::end(kICCSignature), 47 marker->data); 48 } 49 bool MarkerIsExif(const jpeg_saved_marker_ptr marker) { 50 return marker->marker == kExifMarker && 51 marker->data_length >= sizeof kExifSignature + 2 && 52 std::equal(std::begin(kExifSignature), std::end(kExifSignature), 53 marker->data); 54 } 55 56 Status ReadICCProfile(jpeg_decompress_struct* const cinfo, 57 std::vector<uint8_t>* const icc) { 58 constexpr size_t kICCSignatureSize = sizeof kICCSignature; 59 // ICC signature + uint8_t index + uint8_t max_index. 60 constexpr size_t kICCHeadSize = kICCSignatureSize + 2; 61 // Markers are 1-indexed, and we keep them that way in this vector to get a 62 // convenient 0 at the front for when we compute the offsets later. 63 std::vector<size_t> marker_lengths; 64 int num_markers = 0; 65 int seen_markers_count = 0; 66 bool has_num_markers = false; 67 for (jpeg_saved_marker_ptr marker = cinfo->marker_list; marker != nullptr; 68 marker = marker->next) { 69 // marker is initialized by libjpeg, which we are not instrumenting with 70 // msan. 71 msan::UnpoisonMemory(marker, sizeof(*marker)); 72 msan::UnpoisonMemory(marker->data, marker->data_length); 73 if (!MarkerIsICC(marker)) continue; 74 75 const int current_marker = marker->data[kICCSignatureSize]; 76 if (current_marker == 0) { 77 return JXL_FAILURE("inconsistent JPEG ICC marker numbering"); 78 } 79 const int current_num_markers = marker->data[kICCSignatureSize + 1]; 80 if (current_marker > current_num_markers) { 81 return JXL_FAILURE("inconsistent JPEG ICC marker numbering"); 82 } 83 if (has_num_markers) { 84 if (current_num_markers != num_markers) { 85 return JXL_FAILURE("inconsistent numbers of JPEG ICC markers"); 86 } 87 } else { 88 num_markers = current_num_markers; 89 has_num_markers = true; 90 marker_lengths.resize(num_markers + 1); 91 } 92 93 size_t marker_length = marker->data_length - kICCHeadSize; 94 95 if (marker_length == 0) { 96 // NB: if we allow empty chunks, then the next check is incorrect. 97 return JXL_FAILURE("Empty ICC chunk"); 98 } 99 100 if (marker_lengths[current_marker] != 0) { 101 return JXL_FAILURE("duplicate JPEG ICC marker number"); 102 } 103 marker_lengths[current_marker] = marker_length; 104 seen_markers_count++; 105 } 106 107 if (marker_lengths.empty()) { 108 // Not an error. 109 return false; 110 } 111 112 if (seen_markers_count != num_markers) { 113 JXL_ENSURE(has_num_markers); 114 return JXL_FAILURE("Incomplete set of ICC chunks"); 115 } 116 117 std::vector<size_t> offsets = std::move(marker_lengths); 118 std::partial_sum(offsets.begin(), offsets.end(), offsets.begin()); 119 icc->resize(offsets.back()); 120 121 for (jpeg_saved_marker_ptr marker = cinfo->marker_list; marker != nullptr; 122 marker = marker->next) { 123 if (!MarkerIsICC(marker)) continue; 124 const uint8_t* first = marker->data + kICCHeadSize; 125 uint8_t current_marker = marker->data[kICCSignatureSize]; 126 size_t offset = offsets[current_marker - 1]; 127 size_t marker_length = offsets[current_marker] - offset; 128 std::copy_n(first, marker_length, icc->data() + offset); 129 } 130 131 return true; 132 } 133 134 void ReadExif(jpeg_decompress_struct* const cinfo, 135 std::vector<uint8_t>* const exif) { 136 constexpr size_t kExifSignatureSize = sizeof kExifSignature; 137 for (jpeg_saved_marker_ptr marker = cinfo->marker_list; marker != nullptr; 138 marker = marker->next) { 139 // marker is initialized by libjpeg, which we are not instrumenting with 140 // msan. 141 msan::UnpoisonMemory(marker, sizeof(*marker)); 142 msan::UnpoisonMemory(marker->data, marker->data_length); 143 if (!MarkerIsExif(marker)) continue; 144 size_t marker_length = marker->data_length - kExifSignatureSize; 145 exif->resize(marker_length); 146 std::copy_n(marker->data + kExifSignatureSize, marker_length, exif->data()); 147 return; 148 } 149 } 150 151 void MyErrorExit(j_common_ptr cinfo) { 152 jmp_buf* env = static_cast<jmp_buf*>(cinfo->client_data); 153 (*cinfo->err->output_message)(cinfo); 154 jpeg_destroy_decompress(reinterpret_cast<j_decompress_ptr>(cinfo)); 155 longjmp(*env, 1); 156 } 157 158 void MyOutputMessage(j_common_ptr cinfo) { 159 if (JXL_IS_DEBUG_BUILD) { 160 char buf[JMSG_LENGTH_MAX + 1]; 161 (*cinfo->err->format_message)(cinfo, buf); 162 buf[JMSG_LENGTH_MAX] = 0; 163 JXL_WARNING("%s", buf); 164 } 165 } 166 167 Status UnmapColors(uint8_t* row, size_t xsize, int components, 168 JSAMPARRAY colormap, size_t num_colors) { 169 JXL_ENSURE(colormap != nullptr); 170 std::vector<uint8_t> tmp(xsize * components); 171 for (size_t x = 0; x < xsize; ++x) { 172 JXL_ENSURE(row[x] < num_colors); 173 for (int c = 0; c < components; ++c) { 174 tmp[x * components + c] = colormap[c][row[x]]; 175 } 176 } 177 memcpy(row, tmp.data(), tmp.size()); 178 return true; 179 } 180 181 } // namespace 182 #endif 183 184 bool CanDecodeJPG() { 185 #if JPEGXL_ENABLE_JPEG 186 return true; 187 #else 188 return false; 189 #endif 190 } 191 192 Status DecodeImageJPG(const Span<const uint8_t> bytes, 193 const ColorHints& color_hints, PackedPixelFile* ppf, 194 const SizeConstraints* constraints, 195 const JPGDecompressParams* dparams) { 196 #if JPEGXL_ENABLE_JPEG 197 // Don't do anything for non-JPEG files (no need to report an error) 198 if (!IsJPG(bytes)) return false; 199 200 // TODO(veluca): use JPEGData also for pixels? 201 202 // We need to declare all the non-trivial destructor local variables before 203 // the call to setjmp(). 204 std::unique_ptr<JSAMPLE[]> row; 205 206 const auto try_catch_block = [&]() -> bool { 207 jpeg_decompress_struct cinfo = {}; 208 // Setup error handling in jpeg library so we can deal with broken jpegs in 209 // the fuzzer. 210 jpeg_error_mgr jerr; 211 jmp_buf env; 212 cinfo.err = jpeg_std_error(&jerr); 213 jerr.error_exit = &MyErrorExit; 214 jerr.output_message = &MyOutputMessage; 215 if (setjmp(env)) { 216 return false; 217 } 218 cinfo.client_data = static_cast<void*>(&env); 219 220 jpeg_create_decompress(&cinfo); 221 jpeg_mem_src(&cinfo, reinterpret_cast<const unsigned char*>(bytes.data()), 222 bytes.size()); 223 jpeg_save_markers(&cinfo, kICCMarker, 0xFFFF); 224 jpeg_save_markers(&cinfo, kExifMarker, 0xFFFF); 225 const auto failure = [&cinfo](const char* str) -> Status { 226 jpeg_abort_decompress(&cinfo); 227 jpeg_destroy_decompress(&cinfo); 228 return JXL_FAILURE("%s", str); 229 }; 230 int read_header_result = jpeg_read_header(&cinfo, TRUE); 231 // TODO(eustas): what about JPEG_HEADER_TABLES_ONLY? 232 if (read_header_result == JPEG_SUSPENDED) { 233 return failure("truncated JPEG input"); 234 } 235 if (!VerifyDimensions(constraints, cinfo.image_width, cinfo.image_height)) { 236 return failure("image too big"); 237 } 238 // Might cause CPU-zip bomb. 239 if (cinfo.arith_code) { 240 return failure("arithmetic code JPEGs are not supported"); 241 } 242 int nbcomp = cinfo.num_components; 243 if (nbcomp != 1 && nbcomp != 3) { 244 return failure("unsupported number of components in JPEG"); 245 } 246 if (ReadICCProfile(&cinfo, &ppf->icc)) { 247 ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; 248 } else { 249 ppf->primary_color_representation = 250 PackedPixelFile::kColorEncodingIsPrimary; 251 ppf->icc.clear(); 252 // Default to SRGB 253 // Actually, (cinfo.output_components == nbcomp) will be checked after 254 // `jpeg_start_decompress`. 255 ppf->color_encoding.color_space = 256 (nbcomp == 1) ? JXL_COLOR_SPACE_GRAY : JXL_COLOR_SPACE_RGB; 257 ppf->color_encoding.white_point = JXL_WHITE_POINT_D65; 258 ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB; 259 ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB; 260 ppf->color_encoding.rendering_intent = JXL_RENDERING_INTENT_PERCEPTUAL; 261 } 262 ReadExif(&cinfo, &ppf->metadata.exif); 263 if (!ApplyColorHints(color_hints, /*color_already_set=*/true, 264 /*is_gray=*/false, ppf)) { 265 return failure("ApplyColorHints failed"); 266 } 267 268 ppf->info.xsize = cinfo.image_width; 269 ppf->info.ysize = cinfo.image_height; 270 // Original data is uint, so exponent_bits_per_sample = 0. 271 ppf->info.bits_per_sample = BITS_IN_JSAMPLE; 272 static_assert(BITS_IN_JSAMPLE == 8 || BITS_IN_JSAMPLE == 16); 273 ppf->info.exponent_bits_per_sample = 0; 274 ppf->info.uses_original_profile = JXL_TRUE; 275 276 // No alpha in JPG 277 ppf->info.alpha_bits = 0; 278 ppf->info.alpha_exponent_bits = 0; 279 280 ppf->info.num_color_channels = nbcomp; 281 ppf->info.orientation = JXL_ORIENT_IDENTITY; 282 283 if (dparams && dparams->num_colors > 0) { 284 cinfo.quantize_colors = TRUE; 285 cinfo.desired_number_of_colors = dparams->num_colors; 286 cinfo.two_pass_quantize = static_cast<boolean>(dparams->two_pass_quant); 287 cinfo.dither_mode = static_cast<J_DITHER_MODE>(dparams->dither_mode); 288 } 289 290 jpeg_start_decompress(&cinfo); 291 JXL_ENSURE(cinfo.out_color_components == nbcomp); 292 JxlDataType data_type = 293 ppf->info.bits_per_sample <= 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16; 294 295 const JxlPixelFormat format{ 296 /*num_channels=*/static_cast<uint32_t>(nbcomp), 297 data_type, 298 /*endianness=*/JXL_NATIVE_ENDIAN, 299 /*align=*/0, 300 }; 301 ppf->frames.clear(); 302 // Allocates the frame buffer. 303 { 304 JXL_ASSIGN_OR_RETURN( 305 PackedFrame frame, 306 PackedFrame::Create(cinfo.image_width, cinfo.image_height, format)); 307 ppf->frames.emplace_back(std::move(frame)); 308 } 309 const auto& frame = ppf->frames.back(); 310 JXL_ENSURE(sizeof(JSAMPLE) * cinfo.out_color_components * 311 cinfo.image_width <= 312 frame.color.stride); 313 314 if (cinfo.quantize_colors) { 315 JSAMPLE** colormap = cinfo.colormap; 316 jxl::msan::UnpoisonMemory(reinterpret_cast<void*>(colormap), 317 cinfo.out_color_components * sizeof(JSAMPLE*)); 318 for (int c = 0; c < cinfo.out_color_components; ++c) { 319 jxl::msan::UnpoisonMemory( 320 reinterpret_cast<void*>(colormap[c]), 321 cinfo.actual_number_of_colors * sizeof(JSAMPLE)); 322 } 323 } 324 if (dparams && dparams->num_colors > 0) { 325 JXL_ENSURE(cinfo.colormap != nullptr); 326 } 327 for (size_t y = 0; y < cinfo.image_height; ++y) { 328 JSAMPROW rows[] = {reinterpret_cast<JSAMPLE*>( 329 static_cast<uint8_t*>(frame.color.pixels()) + 330 frame.color.stride * y)}; 331 jpeg_read_scanlines(&cinfo, rows, 1); 332 msan::UnpoisonMemory(rows[0], sizeof(JSAMPLE) * cinfo.output_components * 333 cinfo.image_width); 334 if (dparams && dparams->num_colors > 0) { 335 JXL_RETURN_IF_ERROR( 336 UnmapColors(rows[0], cinfo.output_width, cinfo.out_color_components, 337 cinfo.colormap, cinfo.actual_number_of_colors)); 338 } 339 } 340 341 jpeg_finish_decompress(&cinfo); 342 jpeg_destroy_decompress(&cinfo); 343 return true; 344 }; 345 346 return try_catch_block(); 347 #else 348 return false; 349 #endif 350 } 351 352 } // namespace extras 353 } // namespace jxl