jpg.cc (11949B)
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 <jpeglib.h> 10 #include <setjmp.h> 11 #endif 12 #include <stdint.h> 13 14 #include <algorithm> 15 #include <numeric> 16 #include <utility> 17 #include <vector> 18 19 #include "lib/extras/size_constraints.h" 20 #include "lib/jxl/base/status.h" 21 #include "lib/jxl/sanitizers.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_DASSERT(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_DEBUG_WARNING == 1 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 #endif 165 } 166 167 void UnmapColors(uint8_t* row, size_t xsize, int components, 168 JSAMPARRAY colormap, size_t num_colors) { 169 JXL_CHECK(colormap != nullptr); 170 std::vector<uint8_t> tmp(xsize * components); 171 for (size_t x = 0; x < xsize; ++x) { 172 JXL_CHECK(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 } 179 180 } // namespace 181 #endif 182 183 bool CanDecodeJPG() { 184 #if JPEGXL_ENABLE_JPEG 185 return true; 186 #else 187 return false; 188 #endif 189 } 190 191 Status DecodeImageJPG(const Span<const uint8_t> bytes, 192 const ColorHints& color_hints, PackedPixelFile* ppf, 193 const SizeConstraints* constraints, 194 const JPGDecompressParams* dparams) { 195 #if JPEGXL_ENABLE_JPEG 196 // Don't do anything for non-JPEG files (no need to report an error) 197 if (!IsJPG(bytes)) return false; 198 199 // TODO(veluca): use JPEGData also for pixels? 200 201 // We need to declare all the non-trivial destructor local variables before 202 // the call to setjmp(). 203 std::unique_ptr<JSAMPLE[]> row; 204 205 const auto try_catch_block = [&]() -> bool { 206 jpeg_decompress_struct cinfo = {}; 207 // Setup error handling in jpeg library so we can deal with broken jpegs in 208 // the fuzzer. 209 jpeg_error_mgr jerr; 210 jmp_buf env; 211 cinfo.err = jpeg_std_error(&jerr); 212 jerr.error_exit = &MyErrorExit; 213 jerr.output_message = &MyOutputMessage; 214 if (setjmp(env)) { 215 return false; 216 } 217 cinfo.client_data = static_cast<void*>(&env); 218 219 jpeg_create_decompress(&cinfo); 220 jpeg_mem_src(&cinfo, reinterpret_cast<const unsigned char*>(bytes.data()), 221 bytes.size()); 222 jpeg_save_markers(&cinfo, kICCMarker, 0xFFFF); 223 jpeg_save_markers(&cinfo, kExifMarker, 0xFFFF); 224 const auto failure = [&cinfo](const char* str) -> Status { 225 jpeg_abort_decompress(&cinfo); 226 jpeg_destroy_decompress(&cinfo); 227 return JXL_FAILURE("%s", str); 228 }; 229 int read_header_result = jpeg_read_header(&cinfo, TRUE); 230 // TODO(eustas): what about JPEG_HEADER_TABLES_ONLY? 231 if (read_header_result == JPEG_SUSPENDED) { 232 return failure("truncated JPEG input"); 233 } 234 if (!VerifyDimensions(constraints, cinfo.image_width, cinfo.image_height)) { 235 return failure("image too big"); 236 } 237 // Might cause CPU-zip bomb. 238 if (cinfo.arith_code) { 239 return failure("arithmetic code JPEGs are not supported"); 240 } 241 int nbcomp = cinfo.num_components; 242 if (nbcomp != 1 && nbcomp != 3) { 243 return failure("unsupported number of components in JPEG"); 244 } 245 if (ReadICCProfile(&cinfo, &ppf->icc)) { 246 ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; 247 } else { 248 ppf->primary_color_representation = 249 PackedPixelFile::kColorEncodingIsPrimary; 250 ppf->icc.clear(); 251 // Default to SRGB 252 // Actually, (cinfo.output_components == nbcomp) will be checked after 253 // `jpeg_start_decompress`. 254 ppf->color_encoding.color_space = 255 (nbcomp == 1) ? JXL_COLOR_SPACE_GRAY : JXL_COLOR_SPACE_RGB; 256 ppf->color_encoding.white_point = JXL_WHITE_POINT_D65; 257 ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB; 258 ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB; 259 ppf->color_encoding.rendering_intent = JXL_RENDERING_INTENT_PERCEPTUAL; 260 } 261 ReadExif(&cinfo, &ppf->metadata.exif); 262 if (!ApplyColorHints(color_hints, /*color_already_set=*/true, 263 /*is_gray=*/false, ppf)) { 264 return failure("ApplyColorHints failed"); 265 } 266 267 ppf->info.xsize = cinfo.image_width; 268 ppf->info.ysize = cinfo.image_height; 269 // Original data is uint, so exponent_bits_per_sample = 0. 270 ppf->info.bits_per_sample = BITS_IN_JSAMPLE; 271 JXL_ASSERT(BITS_IN_JSAMPLE == 8 || BITS_IN_JSAMPLE == 16); 272 ppf->info.exponent_bits_per_sample = 0; 273 ppf->info.uses_original_profile = JXL_TRUE; 274 275 // No alpha in JPG 276 ppf->info.alpha_bits = 0; 277 ppf->info.alpha_exponent_bits = 0; 278 279 ppf->info.num_color_channels = nbcomp; 280 ppf->info.orientation = JXL_ORIENT_IDENTITY; 281 282 if (dparams && dparams->num_colors > 0) { 283 cinfo.quantize_colors = TRUE; 284 cinfo.desired_number_of_colors = dparams->num_colors; 285 cinfo.two_pass_quantize = static_cast<boolean>(dparams->two_pass_quant); 286 cinfo.dither_mode = static_cast<J_DITHER_MODE>(dparams->dither_mode); 287 } 288 289 jpeg_start_decompress(&cinfo); 290 JXL_ASSERT(cinfo.out_color_components == nbcomp); 291 JxlDataType data_type = 292 ppf->info.bits_per_sample <= 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16; 293 294 const JxlPixelFormat format{ 295 /*num_channels=*/static_cast<uint32_t>(nbcomp), 296 data_type, 297 /*endianness=*/JXL_NATIVE_ENDIAN, 298 /*align=*/0, 299 }; 300 ppf->frames.clear(); 301 // Allocates the frame buffer. 302 ppf->frames.emplace_back(cinfo.image_width, cinfo.image_height, format); 303 const auto& frame = ppf->frames.back(); 304 JXL_ASSERT(sizeof(JSAMPLE) * cinfo.out_color_components * 305 cinfo.image_width <= 306 frame.color.stride); 307 308 if (cinfo.quantize_colors) { 309 JSAMPLE** colormap = cinfo.colormap; 310 jxl::msan::UnpoisonMemory(reinterpret_cast<void*>(colormap), 311 cinfo.out_color_components * sizeof(JSAMPLE*)); 312 for (int c = 0; c < cinfo.out_color_components; ++c) { 313 jxl::msan::UnpoisonMemory( 314 reinterpret_cast<void*>(colormap[c]), 315 cinfo.actual_number_of_colors * sizeof(JSAMPLE)); 316 } 317 } 318 for (size_t y = 0; y < cinfo.image_height; ++y) { 319 JSAMPROW rows[] = {reinterpret_cast<JSAMPLE*>( 320 static_cast<uint8_t*>(frame.color.pixels()) + 321 frame.color.stride * y)}; 322 jpeg_read_scanlines(&cinfo, rows, 1); 323 msan::UnpoisonMemory(rows[0], sizeof(JSAMPLE) * cinfo.output_components * 324 cinfo.image_width); 325 if (dparams && dparams->num_colors > 0) { 326 UnmapColors(rows[0], cinfo.output_width, cinfo.out_color_components, 327 cinfo.colormap, cinfo.actual_number_of_colors); 328 } 329 } 330 331 jpeg_finish_decompress(&cinfo); 332 jpeg_destroy_decompress(&cinfo); 333 return true; 334 }; 335 336 return try_catch_block(); 337 #else 338 return false; 339 #endif 340 } 341 342 } // namespace extras 343 } // namespace jxl