IndexedDB: Protect against key prefix overflows
[WebKit-https.git] / Source / WebKit / chromium / tests / IDBBackingStoreTest.cpp
1 /*
2  * Copyright (C) 2013 Google 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  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27
28 #include "IDBBackingStore.h"
29
30 #include "IDBFactoryBackendImpl.h"
31 #include "IDBLevelDBCoding.h"
32 #include "SharedBuffer.h"
33
34 #include <gtest/gtest.h>
35 #include <webkit/support/webkit_support.h>
36
37 #if ENABLE(INDEXED_DATABASE)
38
39 using namespace WebCore;
40 using IDBLevelDBCoding::KeyPrefix;
41
42 namespace {
43
44 class IDBBackingStoreTest : public testing::Test {
45 public:
46     IDBBackingStoreTest() { }
47     void SetUp()
48     {
49         SecurityOrigin* securityOrigin = 0;
50         // Empty pathBase means an in-memory database.
51         String pathBase;
52         String fileIdentifier;
53         m_backingStore = IDBBackingStore::open(securityOrigin, pathBase, fileIdentifier);
54
55         // useful keys and values during tests
56         const char rawValue1[] = "value1";
57         const char rawValue2[] = "value2";
58         const char rawValue3[] = "value3";
59         m_value1.append(rawValue1, sizeof(rawValue1));
60         m_value2.append(rawValue2, sizeof(rawValue2));
61         m_value3.append(rawValue3, sizeof(rawValue3));
62         m_key1 = IDBKey::createNumber(99);
63         m_key2 = IDBKey::createString("key2");
64         m_key3 = IDBKey::createString("key3");
65     }
66
67 protected:
68     RefPtr<IDBBackingStore> m_backingStore;
69
70     // Sample keys and values that are consistent.
71     RefPtr<IDBKey> m_key1;
72     RefPtr<IDBKey> m_key2;
73     RefPtr<IDBKey> m_key3;
74     Vector<char> m_value1;
75     Vector<char> m_value2;
76     Vector<char> m_value3;
77 };
78
79 TEST_F(IDBBackingStoreTest, PutGetConsistency)
80 {
81     {
82         IDBBackingStore::Transaction transaction1(m_backingStore.get());
83         transaction1.begin();
84         IDBBackingStore::RecordIdentifier record;
85         bool ok = m_backingStore->putRecord(&transaction1, 1, 1, *m_key1.get(), SharedBuffer::create(m_value1.data(), m_value1.size()), &record);
86         EXPECT_TRUE(ok);
87         transaction1.commit();
88     }
89
90     {
91         IDBBackingStore::Transaction transaction2(m_backingStore.get());
92         transaction2.begin();
93         Vector<char> resultValue;
94         bool ok = m_backingStore->getRecord(&transaction2, 1, 1, *m_key1.get(), resultValue);
95         transaction2.commit();
96         EXPECT_TRUE(ok);
97         EXPECT_EQ(m_value1, resultValue);
98     }
99 }
100
101 // Make sure that using very high ( more than 32 bit ) values for databaseId and objectStoreId still work.
102 TEST_F(IDBBackingStoreTest, HighIds)
103 {
104     const int64_t highDatabaseId = 1ULL << 35;
105     const int64_t highObjectStoreId = 1ULL << 39;
106     // indexIds are capped at 32 bits for storage purposes.
107     const int64_t highIndexId = 1ULL << 29;
108
109     const int64_t invalidHighIndexId = 1ULL << 37;
110
111     const RefPtr<IDBKey> indexKey = m_key2;
112     Vector<char> indexKeyRaw = IDBLevelDBCoding::encodeIDBKey(*indexKey);
113     {
114         IDBBackingStore::Transaction transaction1(m_backingStore.get());
115         transaction1.begin();
116         IDBBackingStore::RecordIdentifier record;
117         bool ok = m_backingStore->putRecord(&transaction1, highDatabaseId, highObjectStoreId, *m_key1.get(), SharedBuffer::create(m_value1.data(), m_value1.size()), &record);
118         EXPECT_TRUE(ok);
119
120         ok = m_backingStore->putIndexDataForRecord(&transaction1, highDatabaseId, highObjectStoreId, invalidHighIndexId, *indexKey, record);
121         EXPECT_FALSE(ok);
122
123         ok = m_backingStore->putIndexDataForRecord(&transaction1, highDatabaseId, highObjectStoreId, highIndexId, *indexKey, record);
124         EXPECT_TRUE(ok);
125
126         ok = transaction1.commit();
127         EXPECT_TRUE(ok);
128     }
129
130     {
131         IDBBackingStore::Transaction transaction2(m_backingStore.get());
132         transaction2.begin();
133         Vector<char> resultValue;
134         bool ok = m_backingStore->getRecord(&transaction2, highDatabaseId, highObjectStoreId, *m_key1.get(), resultValue);
135         EXPECT_TRUE(ok);
136         EXPECT_EQ(m_value1, resultValue);
137
138         RefPtr<IDBKey> newPrimaryKey;
139         ok = m_backingStore->getPrimaryKeyViaIndex(&transaction2, highDatabaseId, highObjectStoreId, invalidHighIndexId, *indexKey, newPrimaryKey);
140         EXPECT_FALSE(ok);
141
142         ok = m_backingStore->getPrimaryKeyViaIndex(&transaction2, highDatabaseId, highObjectStoreId, highIndexId, *indexKey, newPrimaryKey);
143         EXPECT_TRUE(ok);
144         EXPECT_TRUE(newPrimaryKey->isEqual(m_key1.get()));
145
146         ok = transaction2.commit();
147         EXPECT_TRUE(ok);
148     }
149 }
150
151 // Make sure that other invalid ids do not crash.
152 TEST_F(IDBBackingStoreTest, InvalidIds)
153 {
154     // valid ids for use when testing invalid ids
155     const int64_t databaseId = 1;
156     const int64_t objectStoreId = 1;
157     const int64_t indexId = IDBLevelDBCoding::MinimumIndexId;
158     const int64_t invalidLowIndexId = 19; // indexIds must be > IDBLevelDBCoding::MinimumIndexId
159
160     const RefPtr<SharedBuffer> value = SharedBuffer::create(m_value1.data(), m_value1.size());
161     Vector<char> resultValue;
162
163     IDBBackingStore::Transaction transaction1(m_backingStore.get());
164     transaction1.begin();
165
166     IDBBackingStore::RecordIdentifier record;
167     bool ok = m_backingStore->putRecord(&transaction1, databaseId, KeyPrefix::InvalidId, *m_key1.get(), value, &record);
168     EXPECT_FALSE(ok);
169     ok = m_backingStore->putRecord(&transaction1, databaseId, 0, *m_key1.get(), value, &record);
170     EXPECT_FALSE(ok);
171     ok = m_backingStore->putRecord(&transaction1, KeyPrefix::InvalidId, objectStoreId, *m_key1.get(), value, &record);
172     EXPECT_FALSE(ok);
173     ok = m_backingStore->putRecord(&transaction1, 0, objectStoreId, *m_key1.get(), value, &record);
174     EXPECT_FALSE(ok);
175
176     ok = m_backingStore->getRecord(&transaction1, databaseId, KeyPrefix::InvalidId, *m_key1.get(), resultValue);
177     EXPECT_FALSE(ok);
178     ok = m_backingStore->getRecord(&transaction1, databaseId, 0, *m_key1.get(), resultValue);
179     EXPECT_FALSE(ok);
180     ok = m_backingStore->getRecord(&transaction1, KeyPrefix::InvalidId, objectStoreId, *m_key1.get(), resultValue);
181     EXPECT_FALSE(ok);
182     ok = m_backingStore->getRecord(&transaction1, 0, objectStoreId, *m_key1.get(), resultValue);
183     EXPECT_FALSE(ok);
184
185     RefPtr<IDBKey> newPrimaryKey;
186     ok = m_backingStore->getPrimaryKeyViaIndex(&transaction1, databaseId, objectStoreId, KeyPrefix::InvalidId, *m_key1, newPrimaryKey);
187     EXPECT_FALSE(ok);
188     ok = m_backingStore->getPrimaryKeyViaIndex(&transaction1, databaseId, objectStoreId, invalidLowIndexId, *m_key1, newPrimaryKey);
189     EXPECT_FALSE(ok);
190     ok = m_backingStore->getPrimaryKeyViaIndex(&transaction1, databaseId, objectStoreId, 0, *m_key1, newPrimaryKey);
191     EXPECT_FALSE(ok);
192
193     ok = m_backingStore->getPrimaryKeyViaIndex(&transaction1, KeyPrefix::InvalidId, objectStoreId, indexId, *m_key1, newPrimaryKey);
194     EXPECT_FALSE(ok);
195     ok = m_backingStore->getPrimaryKeyViaIndex(&transaction1, databaseId, KeyPrefix::InvalidId, indexId, *m_key1, newPrimaryKey);
196     EXPECT_FALSE(ok);
197 }
198
199 TEST_F(IDBBackingStoreTest, CreateDatabase)
200 {
201     const String databaseName("db1");
202     int64_t databaseId;
203     const String version("oldStringVersion");
204     const int64_t intVersion = 9;
205
206     const int64_t objectStoreId = 99;
207     const String objectStoreName("objectStore1");
208     const bool autoIncrement = true;
209     const IDBKeyPath objectStoreKeyPath("objectStoreKey");
210
211     const int64_t indexId = 999;
212     const String indexName("index1");
213     const bool unique = true;
214     const bool multiEntry = true;
215     const IDBKeyPath indexKeyPath("indexKey");
216
217     {
218         bool ok = m_backingStore->createIDBDatabaseMetaData(databaseName, version, intVersion, databaseId);
219         EXPECT_TRUE(ok);
220         EXPECT_GT(databaseId, 0);
221
222         IDBBackingStore::Transaction transaction(m_backingStore.get());
223         transaction.begin();
224
225         ok = m_backingStore->createObjectStore(&transaction, databaseId, objectStoreId, objectStoreName, objectStoreKeyPath, autoIncrement);
226         EXPECT_TRUE(ok);
227
228         ok = m_backingStore->createIndex(&transaction, databaseId, objectStoreId, indexId, indexName, indexKeyPath, unique, multiEntry);
229         EXPECT_TRUE(ok);
230
231         ok = transaction.commit();
232         EXPECT_TRUE(ok);
233     }
234
235     {
236         IDBDatabaseMetadata database;
237         bool found;
238         bool ok = m_backingStore->getIDBDatabaseMetaData(databaseName, &database, found);
239         EXPECT_TRUE(ok);
240         EXPECT_TRUE(found);
241
242         // database.name is not filled in by the implementation.
243         EXPECT_EQ(version, database.version);
244         EXPECT_EQ(intVersion, database.intVersion);
245         EXPECT_EQ(databaseId, database.id);
246
247         ok = m_backingStore->getObjectStores(database.id, &database.objectStores);
248         EXPECT_TRUE(ok);
249
250         EXPECT_EQ(1, database.objectStores.size());
251         IDBObjectStoreMetadata objectStore = database.objectStores.get(objectStoreId);
252         EXPECT_EQ(objectStoreName, objectStore.name);
253         EXPECT_EQ(objectStoreKeyPath, objectStore.keyPath);
254         EXPECT_EQ(autoIncrement, objectStore.autoIncrement);
255
256         EXPECT_EQ(1, objectStore.indexes.size());
257         IDBIndexMetadata index = objectStore.indexes.get(indexId);
258         EXPECT_EQ(indexName, index.name);
259         EXPECT_EQ(indexKeyPath, index.keyPath);
260         EXPECT_EQ(unique, index.unique);
261         EXPECT_EQ(multiEntry, index.multiEntry);
262     }
263 }
264
265 class MockIDBFactoryBackend : public IDBFactoryBackendImpl {
266 public:
267     static PassRefPtr<MockIDBFactoryBackend> create()
268     {
269         return adoptRef(new MockIDBFactoryBackend());
270     }
271
272     PassRefPtr<IDBBackingStore> testOpenBackingStore(PassRefPtr<SecurityOrigin> origin, const String& dataDirectory)
273     {
274         return openBackingStore(origin, dataDirectory);
275     }
276 };
277
278 TEST(IDBFactoryBackendTest, BackingStoreLifetime)
279 {
280     RefPtr<SecurityOrigin> origin1 = SecurityOrigin::create("http", "localhost", 81);
281     RefPtr<SecurityOrigin> origin2 = SecurityOrigin::create("http", "localhost", 82);
282
283     RefPtr<MockIDBFactoryBackend> factory = MockIDBFactoryBackend::create();
284
285     OwnPtr<webkit_support::ScopedTempDirectory> tempDirectory = adoptPtr(webkit_support::CreateScopedTempDirectory());
286     tempDirectory->CreateUniqueTempDir();
287     const String path = String::fromUTF8(tempDirectory->path().c_str());
288
289     RefPtr<IDBBackingStore> diskStore1 = factory->testOpenBackingStore(origin1, path);
290     EXPECT_TRUE(diskStore1->hasOneRef());
291
292     RefPtr<IDBBackingStore> diskStore2 = factory->testOpenBackingStore(origin1, path);
293     EXPECT_EQ(diskStore2.get(), diskStore1.get());
294     EXPECT_EQ(diskStore2->refCount(), 2);
295
296     RefPtr<IDBBackingStore> diskStore3 = factory->testOpenBackingStore(origin2, path);
297     EXPECT_TRUE(diskStore3->hasOneRef());
298     EXPECT_EQ(diskStore1->refCount(), 2);
299 }
300
301 } // namespace
302
303 #endif // ENABLE(INDEXED_DATABASE)