tor-browser

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

EXIF.cpp (12648B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "EXIF.h"
      7 
      8 #include "mozilla/EndianUtils.h"
      9 #include "mozilla/StaticPrefs_image.h"
     10 
     11 namespace mozilla::image {
     12 
     13 // Section references in this file refer to the EXIF v2.3 standard, also known
     14 // as CIPA DC-008-Translation-2010.
     15 
     16 // See Section 4.6.4, Table 4.
     17 // Typesafe enums are intentionally not used here since we're comparing to raw
     18 // integers produced by parsing.
     19 enum class EXIFTag : uint16_t {
     20  Orientation = 0x112,
     21  XResolution = 0x11a,
     22  YResolution = 0x11b,
     23  PixelXDimension = 0xa002,
     24  PixelYDimension = 0xa003,
     25  ResolutionUnit = 0x128,
     26  IFDPointer = 0x8769,
     27 };
     28 
     29 // See Section 4.6.2.
     30 enum EXIFType {
     31  ByteType = 1,
     32  ASCIIType = 2,
     33  ShortType = 3,
     34  LongType = 4,
     35  RationalType = 5,
     36  UndefinedType = 7,
     37  SignedLongType = 9,
     38  SignedRational = 10,
     39 };
     40 
     41 static const char* EXIFHeader = "Exif\0\0";
     42 static const uint32_t EXIFHeaderLength = 6;
     43 
     44 uint32_t EXIFParser::TIFFHeaderStart() const {
     45  return mExpectExifIdCode ? EXIFHeaderLength : 0;
     46 }
     47 
     48 struct ParsedEXIFData {
     49  Orientation orientation;
     50  Maybe<float> resolutionX;
     51  Maybe<float> resolutionY;
     52  Maybe<uint32_t> pixelXDimension;
     53  Maybe<uint32_t> pixelYDimension;
     54  Maybe<ResolutionUnit> resolutionUnit;
     55 };
     56 
     57 static float ToDppx(float aResolution, ResolutionUnit aUnit) {
     58  constexpr float kPointsPerInch = 72.0f;
     59  constexpr float kPointsPerCm = 1.0f / 2.54f;
     60  switch (aUnit) {
     61    case ResolutionUnit::Dpi:
     62      return aResolution / kPointsPerInch;
     63    case ResolutionUnit::Dpcm:
     64      return aResolution / kPointsPerCm;
     65  }
     66  MOZ_CRASH("Unknown resolution unit?");
     67 }
     68 
     69 static Resolution ResolutionFromParsedData(const ParsedEXIFData& aData,
     70                                           const gfx::IntSize& aRealImageSize) {
     71  if (!aData.resolutionUnit || !aData.resolutionX || !aData.resolutionY) {
     72    return {};
     73  }
     74 
     75  Resolution resolution{ToDppx(*aData.resolutionX, *aData.resolutionUnit),
     76                        ToDppx(*aData.resolutionY, *aData.resolutionUnit)};
     77 
     78  if (StaticPrefs::image_exif_density_correction_sanity_check_enabled()) {
     79    if (!aData.pixelXDimension || !aData.pixelYDimension) {
     80      return {};
     81    }
     82 
     83    const gfx::IntSize exifSize(*aData.pixelXDimension, *aData.pixelYDimension);
     84 
     85    gfx::IntSize scaledSize = aRealImageSize;
     86    resolution.ApplyTo(scaledSize.width, scaledSize.height);
     87 
     88    if (exifSize != scaledSize) {
     89      return {};
     90    }
     91  }
     92 
     93  return resolution;
     94 }
     95 
     96 /////////////////////////////////////////////////////////////
     97 // Parse EXIF data, typically found in a JPEG's APP1 segment.
     98 /////////////////////////////////////////////////////////////
     99 EXIFData EXIFParser::ParseEXIF(const uint8_t* aData, const uint32_t aLength,
    100                               const gfx::IntSize& aRealImageSize) {
    101  if (!Initialize(aData, aLength)) {
    102    return EXIFData();
    103  }
    104 
    105  if (mExpectExifIdCode) {
    106    if (!ParseEXIFHeader()) {
    107      return EXIFData();
    108    }
    109  }
    110 
    111  uint32_t offsetIFD;
    112  if (!ParseTIFFHeader(offsetIFD)) {
    113    return EXIFData();
    114  }
    115 
    116  JumpTo(offsetIFD);
    117 
    118  ParsedEXIFData data;
    119  ParseIFD(data);
    120 
    121  return EXIFData{data.orientation,
    122                  ResolutionFromParsedData(data, aRealImageSize)};
    123 }
    124 
    125 /////////////////////////////////////////////////////////
    126 // Parse the EXIF header. (Section 4.7.2, Figure 30)
    127 /////////////////////////////////////////////////////////
    128 bool EXIFParser::ParseEXIFHeader() {
    129  return MatchString(EXIFHeader, EXIFHeaderLength);
    130 }
    131 
    132 /////////////////////////////////////////////////////////
    133 // Parse the TIFF header. (Section 4.5.2, Table 1)
    134 /////////////////////////////////////////////////////////
    135 bool EXIFParser::ParseTIFFHeader(uint32_t& aIFD0OffsetOut) {
    136  // Determine byte order.
    137  if (MatchString("MM\0*", 4)) {
    138    mByteOrder = ByteOrder::BigEndian;
    139  } else if (MatchString("II*\0", 4)) {
    140    mByteOrder = ByteOrder::LittleEndian;
    141  } else {
    142    return false;
    143  }
    144 
    145  // Determine offset of the 0th IFD. (It shouldn't be greater than 64k, which
    146  // is the maximum size of the entry APP1 segment.)
    147  uint32_t ifd0Offset;
    148  if (!ReadUInt32(ifd0Offset) || ifd0Offset > 64 * 1024) {
    149    return false;
    150  }
    151 
    152  // The IFD offset is relative to the beginning of the TIFF header, which
    153  // begins after the EXIF header, so we need to increase the offset
    154  // appropriately.
    155  aIFD0OffsetOut = ifd0Offset + TIFFHeaderStart();
    156  return true;
    157 }
    158 
    159 // An arbitrary limit on the amount of pointers that we'll chase, to prevent bad
    160 // inputs getting us stuck.
    161 constexpr uint32_t kMaxEXIFDepth = 16;
    162 
    163 /////////////////////////////////////////////////////////
    164 // Parse the entries in IFD0. (Section 4.6.2)
    165 /////////////////////////////////////////////////////////
    166 void EXIFParser::ParseIFD(ParsedEXIFData& aData, uint32_t aDepth) {
    167  if (NS_WARN_IF(aDepth > kMaxEXIFDepth)) {
    168    return;
    169  }
    170 
    171  uint16_t entryCount;
    172  if (!ReadUInt16(entryCount)) {
    173    return;
    174  }
    175 
    176  for (uint16_t entry = 0; entry < entryCount; ++entry) {
    177    // Read the fields of the 12-byte entry.
    178    uint16_t tag;
    179    if (!ReadUInt16(tag)) {
    180      return;
    181    }
    182 
    183    uint16_t type;
    184    if (!ReadUInt16(type)) {
    185      return;
    186    }
    187 
    188    uint32_t count;
    189    if (!ReadUInt32(count)) {
    190      return;
    191    }
    192 
    193    switch (EXIFTag(tag)) {
    194      case EXIFTag::Orientation:
    195        // We should have an orientation value here; go ahead and parse it.
    196        if (!ParseOrientation(type, count, aData.orientation)) {
    197          return;
    198        }
    199        break;
    200      case EXIFTag::ResolutionUnit:
    201        if (!ParseResolutionUnit(type, count, aData.resolutionUnit)) {
    202          return;
    203        }
    204        break;
    205      case EXIFTag::XResolution:
    206        if (!ParseResolution(type, count, aData.resolutionX)) {
    207          return;
    208        }
    209        break;
    210      case EXIFTag::YResolution:
    211        if (!ParseResolution(type, count, aData.resolutionY)) {
    212          return;
    213        }
    214        break;
    215      case EXIFTag::PixelXDimension:
    216        if (!ParseDimension(type, count, aData.pixelXDimension)) {
    217          return;
    218        }
    219        break;
    220      case EXIFTag::PixelYDimension:
    221        if (!ParseDimension(type, count, aData.pixelYDimension)) {
    222          return;
    223        }
    224        break;
    225      case EXIFTag::IFDPointer: {
    226        uint32_t offset;
    227        if (!ReadUInt32(offset)) {
    228          return;
    229        }
    230 
    231        ScopedJump jump(*this, offset + TIFFHeaderStart());
    232        ParseIFD(aData, aDepth + 1);
    233        break;
    234      }
    235 
    236      default:
    237        Advance(4);
    238        break;
    239    }
    240  }
    241 }
    242 
    243 bool EXIFParser::ReadRational(float& aOut) {
    244  // Values larger than 4 bytes (like rationals) are specified as an offset into
    245  // the TIFF header.
    246  uint32_t valueOffset;
    247  if (!ReadUInt32(valueOffset)) {
    248    return false;
    249  }
    250  ScopedJump jumpToHeader(*this, valueOffset + TIFFHeaderStart());
    251  uint32_t numerator;
    252  if (!ReadUInt32(numerator)) {
    253    return false;
    254  }
    255  uint32_t denominator;
    256  if (!ReadUInt32(denominator)) {
    257    return false;
    258  }
    259  if (denominator == 0) {
    260    return false;
    261  }
    262  aOut = float(numerator) / float(denominator);
    263  return true;
    264 }
    265 
    266 bool EXIFParser::ParseResolution(uint16_t aType, uint32_t aCount,
    267                                 Maybe<float>& aOut) {
    268  if (aType != RationalType || aCount != 1) {
    269    return false;
    270  }
    271  float value;
    272  if (!ReadRational(value)) {
    273    return false;
    274  }
    275  if (value == 0.0f) {
    276    return false;
    277  }
    278  aOut = Some(value);
    279  return true;
    280 }
    281 
    282 bool EXIFParser::ParseDimension(uint16_t aType, uint32_t aCount,
    283                                Maybe<uint32_t>& aOut) {
    284  if (aCount != 1) {
    285    return false;
    286  }
    287 
    288  switch (aType) {
    289    case ShortType: {
    290      uint16_t value;
    291      if (!ReadUInt16(value)) {
    292        return false;
    293      }
    294      aOut = Some(value);
    295      Advance(2);
    296      break;
    297    }
    298    case LongType: {
    299      uint32_t value;
    300      if (!ReadUInt32(value)) {
    301        return false;
    302      }
    303      aOut = Some(value);
    304      break;
    305    }
    306    default:
    307      return false;
    308  }
    309  return true;
    310 }
    311 
    312 bool EXIFParser::ParseResolutionUnit(uint16_t aType, uint32_t aCount,
    313                                     Maybe<ResolutionUnit>& aOut) {
    314  if (aType != ShortType || aCount != 1) {
    315    return false;
    316  }
    317  uint16_t value;
    318  if (!ReadUInt16(value)) {
    319    return false;
    320  }
    321  switch (value) {
    322    case 2:
    323      aOut = Some(ResolutionUnit::Dpi);
    324      break;
    325    case 3:
    326      aOut = Some(ResolutionUnit::Dpcm);
    327      break;
    328    default:
    329      return false;
    330  }
    331 
    332  // This is a 32-bit field, but the unit value only occupies the first 16 bits.
    333  // We need to advance another 16 bits to consume the entire field.
    334  Advance(2);
    335  return true;
    336 }
    337 
    338 bool EXIFParser::ParseOrientation(uint16_t aType, uint32_t aCount,
    339                                  Orientation& aOut) {
    340  // Sanity check the type and count.
    341  if (aType != ShortType || aCount != 1) {
    342    return false;
    343  }
    344 
    345  uint16_t value;
    346  if (!ReadUInt16(value)) {
    347    return false;
    348  }
    349 
    350  switch (value) {
    351    case 1:
    352      aOut = Orientation(Angle::D0, Flip::Unflipped);
    353      break;
    354    case 2:
    355      aOut = Orientation(Angle::D0, Flip::Horizontal);
    356      break;
    357    case 3:
    358      aOut = Orientation(Angle::D180, Flip::Unflipped);
    359      break;
    360    case 4:
    361      aOut = Orientation(Angle::D180, Flip::Horizontal);
    362      break;
    363    case 5:
    364      aOut = Orientation(Angle::D90, Flip::Horizontal);
    365      break;
    366    case 6:
    367      aOut = Orientation(Angle::D90, Flip::Unflipped);
    368      break;
    369    case 7:
    370      aOut = Orientation(Angle::D270, Flip::Horizontal);
    371      break;
    372    case 8:
    373      aOut = Orientation(Angle::D270, Flip::Unflipped);
    374      break;
    375    default:
    376      return false;
    377  }
    378 
    379  // This is a 32-bit field, but the orientation value only occupies the first
    380  // 16 bits. We need to advance another 16 bits to consume the entire field.
    381  Advance(2);
    382  return true;
    383 }
    384 
    385 bool EXIFParser::Initialize(const uint8_t* aData, const uint32_t aLength) {
    386  if (aData == nullptr) {
    387    return false;
    388  }
    389 
    390  // An APP1 segment larger than 64k violates the JPEG standard.
    391  if (aLength > 64 * 1024) {
    392    return false;
    393  }
    394 
    395  mStart = mCurrent = aData;
    396  mLength = mRemainingLength = aLength;
    397  mByteOrder = ByteOrder::Unknown;
    398  return true;
    399 }
    400 
    401 void EXIFParser::Advance(const uint32_t aDistance) {
    402  if (mRemainingLength >= aDistance) {
    403    mCurrent += aDistance;
    404    mRemainingLength -= aDistance;
    405  } else {
    406    mCurrent = mStart;
    407    mRemainingLength = 0;
    408  }
    409 }
    410 
    411 void EXIFParser::JumpTo(const uint32_t aOffset) {
    412  if (mLength >= aOffset) {
    413    mCurrent = mStart + aOffset;
    414    mRemainingLength = mLength - aOffset;
    415  } else {
    416    mCurrent = mStart;
    417    mRemainingLength = 0;
    418  }
    419 }
    420 
    421 bool EXIFParser::MatchString(const char* aString, const uint32_t aLength) {
    422  if (mRemainingLength < aLength) {
    423    return false;
    424  }
    425 
    426  for (uint32_t i = 0; i < aLength; ++i) {
    427    if (mCurrent[i] != aString[i]) {
    428      return false;
    429    }
    430  }
    431 
    432  Advance(aLength);
    433  return true;
    434 }
    435 
    436 bool EXIFParser::MatchUInt16(const uint16_t aValue) {
    437  if (mRemainingLength < 2) {
    438    return false;
    439  }
    440 
    441  bool matched;
    442  switch (mByteOrder) {
    443    case ByteOrder::LittleEndian:
    444      matched = LittleEndian::readUint16(mCurrent) == aValue;
    445      break;
    446    case ByteOrder::BigEndian:
    447      matched = BigEndian::readUint16(mCurrent) == aValue;
    448      break;
    449    default:
    450      MOZ_ASSERT_UNREACHABLE("Should know the byte order by now");
    451      matched = false;
    452  }
    453 
    454  if (matched) {
    455    Advance(2);
    456  }
    457 
    458  return matched;
    459 }
    460 
    461 bool EXIFParser::ReadUInt16(uint16_t& aValue) {
    462  if (mRemainingLength < 2) {
    463    return false;
    464  }
    465 
    466  bool matched = true;
    467  switch (mByteOrder) {
    468    case ByteOrder::LittleEndian:
    469      aValue = LittleEndian::readUint16(mCurrent);
    470      break;
    471    case ByteOrder::BigEndian:
    472      aValue = BigEndian::readUint16(mCurrent);
    473      break;
    474    default:
    475      MOZ_ASSERT_UNREACHABLE("Should know the byte order by now");
    476      matched = false;
    477  }
    478 
    479  if (matched) {
    480    Advance(2);
    481  }
    482 
    483  return matched;
    484 }
    485 
    486 bool EXIFParser::ReadUInt32(uint32_t& aValue) {
    487  if (mRemainingLength < 4) {
    488    return false;
    489  }
    490 
    491  bool matched = true;
    492  switch (mByteOrder) {
    493    case ByteOrder::LittleEndian:
    494      aValue = LittleEndian::readUint32(mCurrent);
    495      break;
    496    case ByteOrder::BigEndian:
    497      aValue = BigEndian::readUint32(mCurrent);
    498      break;
    499    default:
    500      MOZ_ASSERT_UNREACHABLE("Should know the byte order by now");
    501      matched = false;
    502  }
    503 
    504  if (matched) {
    505    Advance(4);
    506  }
    507 
    508  return matched;
    509 }
    510 
    511 }  // namespace mozilla::image