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