Strings need to be in some kind of gigacage
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / server / IDBSerialization.cpp
1 /*
2  * Copyright (C) 2014, 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 #include "config.h"
26 #include "IDBSerialization.h"
27
28 #if ENABLE(INDEXED_DATABASE)
29
30 #include "IDBKeyData.h"
31 #include "IDBKeyPath.h"
32 #include "KeyedCoding.h"
33
34 #if USE(GLIB)
35 #include <glib.h>
36 #include <wtf/glib/GRefPtr.h>
37 #endif
38
39 namespace WebCore {
40
41 enum class KeyPathType { Null, String, Array };
42
43 RefPtr<SharedBuffer> serializeIDBKeyPath(const std::optional<IDBKeyPath>& keyPath)
44 {
45     auto encoder = KeyedEncoder::encoder();
46
47     if (keyPath) {
48         auto visitor = WTF::makeVisitor([&](const String& string) {
49             encoder->encodeEnum("type", KeyPathType::String);
50             encoder->encodeString("string", string);
51         }, [&](const Vector<String>& vector) {
52             encoder->encodeEnum("type", KeyPathType::Array);
53             encoder->encodeObjects("array", vector.begin(), vector.end(), [](WebCore::KeyedEncoder& encoder, const String& string) {
54                 encoder.encodeString("string", string);
55             });
56         });
57         WTF::visit(visitor, keyPath.value());
58     } else
59         encoder->encodeEnum("type", KeyPathType::Null);
60
61     return encoder->finishEncoding();
62 }
63
64 bool deserializeIDBKeyPath(const uint8_t* data, size_t size, std::optional<IDBKeyPath>& result)
65 {
66     if (!data || !size)
67         return false;
68
69     auto decoder = KeyedDecoder::decoder(data, size);
70
71     KeyPathType type;
72     bool succeeded = decoder->decodeEnum("type", type, [](KeyPathType value) {
73         return value == KeyPathType::Null || value == KeyPathType::String || value == KeyPathType::Array;
74     });
75     if (!succeeded)
76         return false;
77
78     switch (type) {
79     case KeyPathType::Null:
80         break;
81     case KeyPathType::String: {
82         String string;
83         if (!decoder->decodeString("string", string))
84             return false;
85         result = IDBKeyPath(WTFMove(string));
86         break;
87     }
88     case KeyPathType::Array: {
89         Vector<String> vector;
90         succeeded = decoder->decodeObjects("array", vector, [](KeyedDecoder& decoder, String& result) {
91             return decoder.decodeString("string", result);
92         });
93         if (!succeeded)
94             return false;
95         result = IDBKeyPath(WTFMove(vector));
96         break;
97     }
98     }
99     return true;
100 }
101
102 static bool isLegacySerializedIDBKeyData(const uint8_t* data, size_t size)
103 {
104 #if USE(CF)
105     UNUSED_PARAM(size);
106
107     // This is the magic character that begins serialized PropertyLists, and tells us whether
108     // the key we're looking at is an old-style key.
109     static const uint8_t legacySerializedKeyVersion = 'b';
110     if (data[0] == legacySerializedKeyVersion)
111         return true;
112 #elif USE(GLIB)
113     // KeyedEncoderGLib uses a GVariant dictionary, so check if the given data is a valid GVariant dictionary.
114     GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new(data, size));
115     GRefPtr<GVariant> variant = g_variant_new_from_bytes(G_VARIANT_TYPE("a{sv}"), bytes.get(), FALSE);
116     return g_variant_is_normal_form(variant.get());
117 #endif
118     return false;
119 }
120
121
122 /*
123 The IDBKeyData serialization format is as follows:
124 [1 byte version header][Key Buffer]
125
126 The Key Buffer serialization format is as follows:
127 [1 byte key type][Type specific data]
128
129 Type specific serialization formats are as follows for each of the types:
130 Min:
131 [0 bytes]
132
133 Number:
134 [8 bytes representing a double encoded in little endian]
135
136 Date:
137 [8 bytes representing a double encoded in little endian]
138
139 String:
140 [4 bytes representing string "length" in little endian]["length" number of 2-byte pairs representing ECMAScript 16-bit code units]
141
142 Binary:
143 [8 bytes representing the "size" of the binary blob]["size" bytes]
144
145 Array:
146 [8 bytes representing the "length" of the key array]["length" individual Key Buffer entries]
147
148 Max:
149 [0 bytes]
150 */
151
152 static const uint8_t SIDBKeyVersion = 0x00;
153 enum class SIDBKeyType : uint8_t {
154     Min = 0x00,
155     Number = 0x20,
156     Date = 0x40,
157     String = 0x60,
158     Binary = 0x80,
159     Array = 0xA0,
160     Max = 0xFF,
161 };
162
163 static SIDBKeyType serializedTypeForKeyType(IndexedDB::KeyType type)
164 {
165     switch (type) {
166     case IndexedDB::KeyType::Min:
167         return SIDBKeyType::Min;
168     case IndexedDB::KeyType::Number:
169         return SIDBKeyType::Number;
170     case IndexedDB::KeyType::Date:
171         return SIDBKeyType::Date;
172     case IndexedDB::KeyType::String:
173         return SIDBKeyType::String;
174     case IndexedDB::KeyType::Binary:
175         return SIDBKeyType::Binary;
176     case IndexedDB::KeyType::Array:
177         return SIDBKeyType::Array;
178     case IndexedDB::KeyType::Max:
179         return SIDBKeyType::Max;
180     case IndexedDB::KeyType::Invalid:
181         RELEASE_ASSERT_NOT_REACHED();
182     };
183
184     RELEASE_ASSERT_NOT_REACHED();
185 }
186
187 #if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) || CPU(NEEDS_ALIGNED_ACCESS)
188 template <typename T> static void writeLittleEndian(Vector<char>& buffer, T value)
189 {
190     for (unsigned i = 0; i < sizeof(T); i++) {
191         buffer.append(value & 0xFF);
192         value >>= 8;
193     }
194 }
195
196 template <typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value)
197 {
198     if (ptr > end - sizeof(value))
199         return false;
200
201     value = 0;
202     for (size_t i = 0; i < sizeof(T); i++)
203         value += ((T)*ptr++) << (i * 8);
204     return true;
205 }
206 #else
207 template <typename T> static void writeLittleEndian(Vector<char>& buffer, T value)
208 {
209     buffer.append(reinterpret_cast<uint8_t*>(&value), sizeof(value));
210 }
211
212 template <typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value)
213 {
214     if (ptr > end - sizeof(value))
215         return false;
216
217     value = *reinterpret_cast<const T*>(ptr);
218     ptr += sizeof(T);
219
220     return true;
221 }
222 #endif
223
224 static void writeDouble(Vector<char>& data, double d)
225 {
226     writeLittleEndian(data, *reinterpret_cast<uint64_t*>(&d));
227 }
228
229 static bool readDouble(const uint8_t*& data, const uint8_t* end, double& d)
230 {
231     return readLittleEndian(data, end, *reinterpret_cast<uint64_t*>(&d));
232 }
233
234 static void encodeKey(Vector<char>& data, const IDBKeyData& key)
235 {
236     SIDBKeyType type = serializedTypeForKeyType(key.type());
237     data.append(static_cast<char>(type));
238
239     switch (type) {
240     case SIDBKeyType::Number:
241         writeDouble(data, key.number());
242         break;
243     case SIDBKeyType::Date:
244         writeDouble(data, key.date());
245         break;
246     case SIDBKeyType::String: {
247         auto string = key.string();
248         uint32_t length = string.length();
249         writeLittleEndian(data, length);
250
251         for (size_t i = 0; i < length; ++i)
252             writeLittleEndian(data, string[i]);
253
254         break;
255     }
256     case SIDBKeyType::Binary: {
257         auto& buffer = key.binary();
258         uint64_t size = buffer.size();
259         writeLittleEndian(data, size);
260
261         auto* bufferData = buffer.data();
262         ASSERT(bufferData || !size);
263         if (bufferData)
264             data.append(bufferData->data(), bufferData->size());
265
266         break;
267     }
268     case SIDBKeyType::Array: {
269         auto& array = key.array();
270         uint64_t size = array.size();
271         writeLittleEndian(data, size);
272         for (auto& key : array)
273             encodeKey(data, key);
274
275         break;
276     }
277     case SIDBKeyType::Min:
278     case SIDBKeyType::Max:
279         break;
280     }
281 }
282
283 RefPtr<SharedBuffer> serializeIDBKeyData(const IDBKeyData& key)
284 {
285     Vector<char> data;
286     data.append(SIDBKeyVersion);
287
288     encodeKey(data, key);
289     return SharedBuffer::create(WTFMove(data));
290 }
291
292 static bool decodeKey(const uint8_t*& data, const uint8_t* end, IDBKeyData& result)
293 {
294     if (!data || data >= end)
295         return false;
296
297     SIDBKeyType type = static_cast<SIDBKeyType>(data++[0]);
298     switch (type) {
299     case SIDBKeyType::Min:
300         result = IDBKeyData::minimum();
301         return true;
302     case SIDBKeyType::Max:
303         result = IDBKeyData::maximum();
304         return true;
305     case SIDBKeyType::Number: {
306         double d;
307         if (!readDouble(data, end, d))
308             return false;
309
310         result.setNumberValue(d);
311         return true;
312     }
313     case SIDBKeyType::Date: {
314         double d;
315         if (!readDouble(data, end, d))
316             return false;
317
318         result.setDateValue(d);
319         return true;
320     }
321     case SIDBKeyType::String: {
322         uint32_t length;
323         if (!readLittleEndian(data, end, length))
324             return false;
325
326         if (static_cast<uint64_t>(end - data) < length * 2)
327             return false;
328
329         StringVector<UChar> buffer;
330         buffer.reserveInitialCapacity(length);
331         for (size_t i = 0; i < length; i++) {
332             uint16_t ch;
333             if (!readLittleEndian(data, end, ch))
334                 return false;
335             buffer.uncheckedAppend(ch);
336         }
337
338         result.setStringValue(String::adopt(WTFMove(buffer)));
339
340         return true;
341     }
342     case SIDBKeyType::Binary: {
343         uint64_t size64;
344         if (!readLittleEndian(data, end, size64))
345             return false;
346
347         if (static_cast<uint64_t>(end - data) < size64)
348             return false;
349
350         if (size64 > std::numeric_limits<size_t>::max())
351             return false;
352
353         size_t size = static_cast<size_t>(size64);
354         Vector<uint8_t> dataVector;
355
356         dataVector.append(data, size);
357         data += size;
358
359         result.setBinaryValue(ThreadSafeDataBuffer::create(WTFMove(dataVector)));
360         return true;
361     }
362     case SIDBKeyType::Array: {
363         uint64_t size64;
364         if (!readLittleEndian(data, end, size64))
365             return false;
366
367         if (size64 > std::numeric_limits<size_t>::max())
368             return false;
369
370         size_t size = static_cast<size_t>(size64);
371         Vector<IDBKeyData> array;
372         array.reserveInitialCapacity(size);
373
374         for (size_t i = 0; i < size; ++i) {
375             IDBKeyData keyData;
376             if (!decodeKey(data, end, keyData))
377                 return false;
378
379             ASSERT(keyData.isValid());
380             array.uncheckedAppend(WTFMove(keyData));
381         }
382
383         result.setArrayValue(array);
384
385         return true;
386     }
387     default:
388         LOG_ERROR("decodeKey encountered unexpected type: %i", (int)type);
389         return false;
390     }
391 }
392
393 bool deserializeIDBKeyData(const uint8_t* data, size_t size, IDBKeyData& result)
394 {
395     if (!data || !size)
396         return false;
397
398     if (isLegacySerializedIDBKeyData(data, size)) {
399         auto decoder = KeyedDecoder::decoder(data, size);
400         return IDBKeyData::decode(*decoder, result);
401     }
402
403     // Verify this is a SerializedIDBKey version we understand.
404     const uint8_t* current = data;
405     const uint8_t* end = data + size;
406     if (current++[0] != SIDBKeyVersion)
407         return false;
408
409     if (decodeKey(current, end, result)) {
410         // Even if we successfully decoded a key, the deserialize is only successful
411         // if we actually consumed all input data.
412         return current == end;
413     }
414
415     return false;
416 }
417
418 } // namespace WebCore
419
420 #endif // ENABLE(INDEXED_DATABASE)