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