• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/codec/SkTiffUtility.h"
9 
10 #include "include/core/SkData.h"
11 #include "src/codec/SkCodecPriv.h"
12 
13 #include <cstddef>
14 #include <utility>
15 
16 namespace SkTiff {
17 
18 constexpr size_t kSizeEntry = 12;
19 constexpr size_t kSizeShort = 2;
20 constexpr size_t kSizeLong = 4;
21 
IsValidType(uint16_t type)22 bool ImageFileDirectory::IsValidType(uint16_t type) { return type >= 1 && type <= 12; }
23 
BytesForType(uint16_t type)24 size_t ImageFileDirectory::BytesForType(uint16_t type) {
25     switch (type) {
26         case kTypeUnsignedByte:
27             return 1;
28         case kTypeAsciiString:
29             return 1;
30         case kTypeUnsignedShort:
31             return kSizeShort;
32         case kTypeUnsignedLong:
33             return kSizeLong;
34         case kTypeUnsignedRational:
35             return 8;
36         case kTypeSignedByte:
37             return 1;
38         case kTypeUndefined:
39             return 1;
40         case kTypeSignedShort:
41             return kSizeShort;
42         case kTypeSignedLong:
43             return kSizeLong;
44         case kTypeSignedRational:
45             return 8;
46         case kTypeSingleFloat:
47             return 4;
48         case kTypeDoubleFloat:
49             return 8;
50     }
51     return 0;
52 }
53 
54 // Helper function for computing the address of an entry.
get_entry_address(const SkData * data,uint32_t ifdOffset,uint16_t entryIndex)55 static const uint8_t* get_entry_address(const SkData* data,
56                                         uint32_t ifdOffset,
57                                         uint16_t entryIndex) {
58     return data->bytes() +           // Base address
59            ifdOffset +               // IFD offset
60            kSizeShort +              // IFD number of entries
61            kSizeEntry * entryIndex;  // Entries
62 }
63 
64 // Return true if the IFD starting at |ifdOffset| contains valid number of entries (that doesn't
65 // overrun |data|).
validate_ifd(const SkData * data,bool littleEndian,uint32_t ifdOffset,bool allowTruncated,uint16_t * outNumEntries,uint32_t * outNextIfdOffset)66 static bool validate_ifd(const SkData* data,
67                          bool littleEndian,
68                          uint32_t ifdOffset,
69                          bool allowTruncated,
70                          uint16_t* outNumEntries,
71                          uint32_t* outNextIfdOffset) {
72     const uint8_t* dataCurrent = data->bytes();
73     size_t dataSize = data->size();
74 
75     // Seek to the IFD offset.
76     if (dataSize < ifdOffset) {
77         SkCodecPrintf("IFD offset is too large.\n");
78         return false;
79     }
80     dataCurrent += ifdOffset;
81     dataSize -= ifdOffset;
82 
83     // Read the number of entries.
84     if (dataSize < kSizeShort) {
85         SkCodecPrintf("Insufficient space to store number of entries.\n");
86         return false;
87     }
88     uint16_t numEntries = get_endian_short(dataCurrent, littleEndian);
89     dataCurrent += kSizeShort;
90     dataSize -= kSizeShort;
91 
92     // Check that there is enough space for all entries.
93     if (dataSize < kSizeEntry * numEntries) {
94         SkCodecPrintf("Insufficient space (%u) to store all %u entries.\n",
95                       static_cast<uint32_t>(data->size()),
96                       numEntries);
97         if (allowTruncated) {
98             // Set the number of entries to the number of entries that can be fully read, and set
99             // the next IFD offset to 0 (indicating that there is no next IFD).
100             *outNumEntries = dataSize / kSizeEntry;
101             *outNextIfdOffset = 0;
102             return true;
103         }
104         return false;
105     }
106 
107     // Save the number of entries.
108     *outNumEntries = numEntries;
109 
110     // Seek past the entries.
111     dataCurrent += kSizeEntry * numEntries;
112     dataSize -= kSizeEntry * numEntries;
113 
114     // Read the next IFD offset.
115     if (dataSize < kSizeLong) {
116         SkCodecPrintf("Insufficient space to store next IFD offset.\n");
117         if (allowTruncated) {
118             // Set the next IFD offset to 0 (indicating that there is no next IFD).
119             *outNextIfdOffset = 0;
120             return true;
121         }
122         return false;
123     }
124 
125     // Save the next IFD offset.
126     *outNextIfdOffset = get_endian_int(dataCurrent, littleEndian);
127     return true;
128 }
129 
ParseHeader(const SkData * data,bool * outLittleEndian,uint32_t * outIfdOffset)130 bool ImageFileDirectory::ParseHeader(const SkData* data,
131                                      bool* outLittleEndian,
132                                      uint32_t* outIfdOffset) {
133     // Read the endianness (4 bytes) and IFD offset (4 bytes).
134     if (data->size() < 8) {
135         SkCodecPrintf("Tiff header must be at least 8 bytes.\n");
136         return false;
137     }
138     if (!is_valid_endian_marker(data->bytes(), outLittleEndian)) {
139         SkCodecPrintf("Tiff header had invalid endian marker 0x%x,0x%x,0x%x,0x%x.\n",
140                       data->bytes()[0],
141                       data->bytes()[1],
142                       data->bytes()[2],
143                       data->bytes()[3]);
144         return false;
145     }
146     *outIfdOffset = get_endian_int(data->bytes() + 4, *outLittleEndian);
147     return true;
148 }
149 
MakeFromOffset(sk_sp<SkData> data,bool littleEndian,uint32_t ifdOffset,bool allowTruncated)150 std::unique_ptr<ImageFileDirectory> ImageFileDirectory::MakeFromOffset(sk_sp<SkData> data,
151                                                                        bool littleEndian,
152                                                                        uint32_t ifdOffset,
153                                                                        bool allowTruncated) {
154     uint16_t numEntries = 0;
155     uint32_t nextOffset = 0;
156     if (!validate_ifd(
157                 data.get(), littleEndian, ifdOffset, allowTruncated, &numEntries, &nextOffset)) {
158         SkCodecPrintf("Failed to validate IFD.\n");
159         return nullptr;
160     }
161     return std::unique_ptr<ImageFileDirectory>(new ImageFileDirectory(
162             std::move(data), littleEndian, ifdOffset, numEntries, nextOffset));
163 }
164 
ImageFileDirectory(sk_sp<SkData> data,bool littleEndian,uint32_t offset,uint16_t numEntries,uint32_t nextIfdOffset)165 ImageFileDirectory::ImageFileDirectory(sk_sp<SkData> data,
166                                        bool littleEndian,
167                                        uint32_t offset,
168                                        uint16_t numEntries,
169                                        uint32_t nextIfdOffset)
170         : fData(std::move(data))
171         , fLittleEndian(littleEndian)
172         , fOffset(offset)
173         , fNumEntries(numEntries)
174         , fNextIfdOffset(nextIfdOffset) {}
175 
getEntryTag(uint16_t entryIndex) const176 uint16_t ImageFileDirectory::getEntryTag(uint16_t entryIndex) const {
177     const uint8_t* entry = get_entry_address(fData.get(), fOffset, entryIndex);
178     return get_endian_short(entry, fLittleEndian);
179 }
180 
getEntryRawData(uint16_t entryIndex,uint16_t * outTag,uint16_t * outType,uint32_t * outCount,const uint8_t ** outData,size_t * outDataSize) const181 bool ImageFileDirectory::getEntryRawData(uint16_t entryIndex,
182                                          uint16_t* outTag,
183                                          uint16_t* outType,
184                                          uint32_t* outCount,
185                                          const uint8_t** outData,
186                                          size_t* outDataSize) const {
187     const uint8_t* entry = get_entry_address(fData.get(), fOffset, entryIndex);
188 
189     // Read the tag
190     const uint16_t tag = get_endian_short(entry, fLittleEndian);
191     entry += kSizeShort;
192 
193     // Read the type.
194     const uint16_t type = get_endian_short(entry, fLittleEndian);
195     entry += kSizeShort;
196     if (!IsValidType(type)) {
197         return false;
198     }
199 
200     // Read the count.
201     const uint32_t count = get_endian_int(entry, fLittleEndian);
202     entry += kSizeLong;
203 
204     // If the entry fits in the remaining 4 bytes, use that.
205     const size_t entryDataBytes = BytesForType(type) * count;
206     const uint8_t* entryData = nullptr;
207     if (entryDataBytes <= kSizeLong) {
208         entryData = entry;
209     } else {
210         // Otherwise, the next 4 bytes specify an offset where the data can be found.
211         const uint32_t entryDataOffset = get_endian_int(entry, fLittleEndian);
212         if (fData->size() < entryDataOffset || fData->size() - entryDataOffset < entryDataBytes) {
213             return false;
214         }
215         entryData = fData->bytes() + entryDataOffset;
216     }
217 
218     if (outTag) *outTag = tag;
219     if (outType) *outType = type;
220     if (outCount) *outCount = count;
221     if (outData) *outData = entryData;
222     if (outDataSize) *outDataSize = entryDataBytes;
223     return true;
224 }
225 
getEntryUndefinedData(uint16_t entryIndex) const226 sk_sp<SkData> ImageFileDirectory::getEntryUndefinedData(uint16_t entryIndex) const {
227     uint16_t type = 0;
228     uint32_t count = 0;
229     const uint8_t* data = nullptr;
230     size_t size = 0;
231     if (!getEntryRawData(entryIndex, nullptr, &type, &count, &data, &size)) {
232         return nullptr;
233     }
234     if (type != kTypeUndefined) {
235         return nullptr;
236     }
237     return SkData::MakeSubset(fData.get(), data - fData->bytes(), size);
238 }
239 
getEntryValuesGeneric(uint16_t entryIndex,uint16_t type,uint32_t count,void * values) const240 bool ImageFileDirectory::getEntryValuesGeneric(uint16_t entryIndex,
241                                                uint16_t type,
242                                                uint32_t count,
243                                                void* values) const {
244     uint16_t entryType = 0;
245     uint32_t entryCount = 0;
246     const uint8_t* entryData = nullptr;
247     if (!getEntryRawData(entryIndex, nullptr, &entryType, &entryCount, &entryData, nullptr)) {
248         return false;
249     }
250     if (type != entryType) {
251         return false;
252     }
253     if (count != entryCount) {
254         return false;
255     }
256     for (uint32_t i = 0; i < count; ++i) {
257         const uint8_t* data = entryData + i * BytesForType(kTypeUnsignedLong);
258         switch (type) {
259             case kTypeUnsignedShort:
260                 reinterpret_cast<uint16_t*>(values)[i] = get_endian_short(data, fLittleEndian);
261                 break;
262             case kTypeUnsignedLong:
263                 reinterpret_cast<uint32_t*>(values)[i] = get_endian_int(data, fLittleEndian);
264                 break;
265             case kTypeSignedRational: {
266                 uint32_t numerator = get_endian_int(data, fLittleEndian);
267                 uint32_t denominator = get_endian_int(data + kSizeLong, fLittleEndian);
268                 if (denominator == 0) {
269                     // The TIFF specification does not indicate a behavior when the denominator is
270                     // zero.  The behavior of returning zero for a denominator of zero is a
271                     // preservation of the behavior introduced in https://crrev.com/767874.
272                     reinterpret_cast<float*>(values)[i] = 0;
273                 } else {
274                     reinterpret_cast<float*>(values)[i] =
275                             numerator / static_cast<float>(denominator);
276                 }
277                 break;
278             }
279             case kTypeUnsignedRational: {
280                 uint32_t numerator = get_endian_int(data, fLittleEndian);
281                 uint32_t denominator = get_endian_int(data + kSizeLong, fLittleEndian);
282                 if (denominator == 0) {
283                     // See comments in kTypeSignedRational.
284                     reinterpret_cast<float*>(values)[i] = 0.f;
285                 } else {
286                     reinterpret_cast<float*>(values)[i] =
287                             numerator / static_cast<float>(denominator);
288                 }
289                 break;
290             }
291             default:
292                 SkCodecPrintf("Unsupported type %u\n", type);
293                 return false;
294                 break;
295         }
296     }
297     return true;
298 }
299 
300 }  // namespace SkTiff
301