image/decoders/EXIF.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     6 #include "EXIF.h"
     8 #include "mozilla/Endian.h"
    10 namespace mozilla {
    11 namespace image {
    13 // Section references in this file refer to the EXIF v2.3 standard, also known
    14 // as CIPA DC-008-Translation-2010.
    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 EXIFTag
    20 {
    21   OrientationTag = 0x112,
    22 };
    24 // See Section 4.6.2.
    25 enum EXIFType
    26 {
    27   ByteType       = 1,
    28   ASCIIType      = 2,
    29   ShortType      = 3,
    30   LongType       = 4,
    31   RationalType   = 5,
    32   UndefinedType  = 7,
    33   SignedLongType = 9,
    34   SignedRational = 10,
    35 };
    37 static const char* EXIFHeader = "Exif\0\0";
    38 static const uint32_t EXIFHeaderLength = 6;
    40 /////////////////////////////////////////////////////////////
    41 // Parse EXIF data, typically found in a JPEG's APP1 segment.
    42 /////////////////////////////////////////////////////////////
    43 EXIFData
    44 EXIFParser::ParseEXIF(const uint8_t* aData, const uint32_t aLength)
    45 {
    46   if (!Initialize(aData, aLength))
    47     return EXIFData();
    49   if (!ParseEXIFHeader())
    50     return EXIFData();
    52   uint32_t offsetIFD;
    53   if (!ParseTIFFHeader(offsetIFD))
    54     return EXIFData();
    56   JumpTo(offsetIFD);
    58   Orientation orientation;
    59   if (!ParseIFD0(orientation))
    60     return EXIFData();
    62   // We only care about orientation at this point, so we don't bother with the
    63   // other IFDs. If we got this far we're done.
    64   return EXIFData(orientation);
    65 }
    67 /////////////////////////////////////////////////////////
    68 // Parse the EXIF header. (Section 4.7.2, Figure 30)
    69 /////////////////////////////////////////////////////////
    70 bool
    71 EXIFParser::ParseEXIFHeader()
    72 {
    73   return MatchString(EXIFHeader, EXIFHeaderLength);
    74 }
    76 /////////////////////////////////////////////////////////
    77 // Parse the TIFF header. (Section 4.5.2, Table 1)
    78 /////////////////////////////////////////////////////////
    79 bool
    80 EXIFParser::ParseTIFFHeader(uint32_t& aIFD0OffsetOut)
    81 {
    82   // Determine byte order.
    83   if (MatchString("MM\0*", 4))
    84     mByteOrder = ByteOrder::BigEndian;
    85   else if (MatchString("II*\0", 4))
    86     mByteOrder = ByteOrder::LittleEndian;
    87   else
    88     return false;
    90   // Determine offset of the 0th IFD. (It shouldn't be greater than 64k, which
    91   // is the maximum size of the entry APP1 segment.)
    92   uint32_t ifd0Offset;
    93   if (!ReadUInt32(ifd0Offset) || ifd0Offset > 64 * 1024)
    94     return false;
    96   // The IFD offset is relative to the beginning of the TIFF header, which
    97   // begins after the EXIF header, so we need to increase the offset
    98   // appropriately.
    99   aIFD0OffsetOut = ifd0Offset + EXIFHeaderLength;
   100   return true;
   101 }
   103 /////////////////////////////////////////////////////////
   104 // Parse the entries in IFD0. (Section 4.6.2)
   105 /////////////////////////////////////////////////////////
   106 bool
   107 EXIFParser::ParseIFD0(Orientation& aOrientationOut)
   108 {
   109   uint16_t entryCount;
   110   if (!ReadUInt16(entryCount))
   111     return false;
   113   for (uint16_t entry = 0 ; entry < entryCount ; ++entry) {
   114     // Read the fields of the entry.
   115     uint16_t tag;
   116     if (!ReadUInt16(tag))
   117       return false;
   119     // Right now, we only care about orientation, so we immediately skip to the
   120     // next entry if we find anything else.
   121     if (tag != OrientationTag) {
   122       Advance(10);
   123       continue;
   124     }
   126     uint16_t type;
   127     if (!ReadUInt16(type))
   128       return false;
   130     uint32_t count;
   131     if (!ReadUInt32(count))
   132       return false;
   134     // We should have an orientation value here; go ahead and parse it.
   135     Orientation orientation;
   136     if (!ParseOrientation(type, count, aOrientationOut))
   137       return false;
   139     // Since the orientation is all we care about, we're done.
   140     return true;
   141   }
   143   // We didn't find an orientation field in the IFD. That's OK; we assume the
   144   // default orientation in that case.
   145   aOrientationOut = Orientation();
   146   return true;
   147 }
   149 bool
   150 EXIFParser::ParseOrientation(uint16_t aType, uint32_t aCount, Orientation& aOut)
   151 {
   152   // Sanity check the type and count.
   153   if (aType != ShortType || aCount != 1)
   154     return false;
   156   uint16_t value;
   157   if (!ReadUInt16(value))
   158     return false;
   160   switch (value) {
   161     case 1: aOut = Orientation(Angle::D0,   Flip::Unflipped);  break;
   162     case 2: aOut = Orientation(Angle::D0,   Flip::Horizontal); break;
   163     case 3: aOut = Orientation(Angle::D180, Flip::Unflipped);  break;
   164     case 4: aOut = Orientation(Angle::D180, Flip::Horizontal); break;
   165     case 5: aOut = Orientation(Angle::D90,  Flip::Horizontal); break;
   166     case 6: aOut = Orientation(Angle::D90,  Flip::Unflipped);  break;
   167     case 7: aOut = Orientation(Angle::D270, Flip::Horizontal); break;
   168     case 8: aOut = Orientation(Angle::D270, Flip::Unflipped);  break;
   169     default: return false;
   170   }
   172   // This is a 32-bit field, but the orientation value only occupies the first
   173   // 16 bits. We need to advance another 16 bits to consume the entire field.
   174   Advance(2);
   175   return true;
   176 }
   178 bool
   179 EXIFParser::Initialize(const uint8_t* aData, const uint32_t aLength)
   180 {
   181   if (aData == nullptr)
   182     return false;
   184   // An APP1 segment larger than 64k violates the JPEG standard.
   185   if (aLength > 64 * 1024)
   186     return false;
   188   mStart = mCurrent = aData;
   189   mLength = mRemainingLength = aLength;
   190   mByteOrder = ByteOrder::Unknown;
   191   return true;
   192 }
   194 void
   195 EXIFParser::Advance(const uint32_t aDistance)
   196 {
   197   if (mRemainingLength >= aDistance) {
   198     mCurrent += aDistance;
   199     mRemainingLength -= aDistance;
   200   } else {
   201     mCurrent = mStart;
   202     mRemainingLength = 0;
   203   }
   204 }
   206 void
   207 EXIFParser::JumpTo(const uint32_t aOffset)
   208 {
   209   if (mLength >= aOffset) {
   210     mCurrent = mStart + aOffset;
   211     mRemainingLength = mLength - aOffset;
   212   } else {
   213     mCurrent = mStart;
   214     mRemainingLength = 0;
   215   }
   216 }
   218 bool
   219 EXIFParser::MatchString(const char* aString, const uint32_t aLength)
   220 {
   221   if (mRemainingLength < aLength)
   222     return false;
   224   for (uint32_t i = 0 ; i < aLength ; ++i) {
   225     if (mCurrent[i] != aString[i])
   226       return false;
   227   }
   229   Advance(aLength);
   230   return true;
   231 }
   233 bool
   234 EXIFParser::MatchUInt16(const uint16_t aValue)
   235 {
   236   if (mRemainingLength < 2)
   237     return false;
   239   bool matched;
   240   switch (mByteOrder) {
   241     case ByteOrder::LittleEndian:
   242       matched = LittleEndian::readUint16(mCurrent) == aValue;
   243       break;
   244     case ByteOrder::BigEndian:
   245       matched = BigEndian::readUint16(mCurrent) == aValue;
   246       break;
   247     default:
   248       NS_NOTREACHED("Should know the byte order by now");
   249       matched = false;
   250   }
   252   if (matched)
   253     Advance(2);
   255   return matched;
   256 }
   258 bool
   259 EXIFParser::ReadUInt16(uint16_t& aValue)
   260 {
   261   if (mRemainingLength < 2)
   262     return false;
   264   bool matched = true;
   265   switch (mByteOrder) {
   266     case ByteOrder::LittleEndian:
   267       aValue = LittleEndian::readUint16(mCurrent);
   268       break;
   269     case ByteOrder::BigEndian:
   270       aValue = BigEndian::readUint16(mCurrent);
   271       break;
   272     default:
   273       NS_NOTREACHED("Should know the byte order by now");
   274       matched = false;
   275   }
   277   if (matched)
   278     Advance(2);
   280   return matched;
   281 }
   283 bool
   284 EXIFParser::ReadUInt32(uint32_t& aValue)
   285 {
   286   if (mRemainingLength < 4)
   287     return false;
   289   bool matched = true;
   290   switch (mByteOrder) {
   291     case ByteOrder::LittleEndian:
   292       aValue = LittleEndian::readUint32(mCurrent);
   293       break;
   294     case ByteOrder::BigEndian:
   295       aValue = BigEndian::readUint32(mCurrent);
   296       break;
   297     default:
   298       NS_NOTREACHED("Should know the byte order by now");
   299       matched = false;
   300   }
   302   if (matched)
   303     Advance(4);
   305   return matched;
   306 }
   308 } // namespace image
   309 } // namespace mozilla

mercurial