IndexedDB: Protect against key prefix overflows
authoralecflett@chromium.org <alecflett@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Mar 2013 23:57:21 +0000 (23:57 +0000)
committeralecflett@chromium.org <alecflett@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Mar 2013 23:57:21 +0000 (23:57 +0000)
https://bugs.webkit.org/show_bug.cgi?id=111138

Reviewed by Tony Chang.

Source/WebCore:

This reworks the boundary checking for all databaseId,
objectStoreId, and indexId, including negative and
zero-based ids. All entrypoints into IDBLevelDBCoding
are protected with explicit checks and all internal
uses of KeyPrefix are protected with ASSERTs in the
various constructors.

Tests: WebKit unit tests IDBBackingStoreTest.cpp in WebKit/chromium

* Modules/indexeddb/IDBBackingStore.h: Make all public methods boolean-based for errors.
* Modules/indexeddb/IDBLevelDBCoding.h: Add methods for checking databaseId, objectStoreId, and indexId.

Source/WebKit/chromium:

Add tests for invalid indexIds in basic get/put operations.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@145435 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/Modules/indexeddb/IDBBackingStore.cpp
Source/WebCore/Modules/indexeddb/IDBBackingStore.h
Source/WebCore/Modules/indexeddb/IDBDatabaseBackendImpl.cpp
Source/WebCore/Modules/indexeddb/IDBLevelDBCoding.cpp
Source/WebCore/Modules/indexeddb/IDBLevelDBCoding.h
Source/WebCore/Modules/indexeddb/IDBObjectStoreBackendImpl.cpp
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/tests/IDBBackingStoreTest.cpp
Source/WebKit/chromium/tests/IDBFakeBackingStore.h
Source/WebKit/chromium/tests/IDBLevelDBCodingTest.cpp

index 79ee46e..9fc490e 100644 (file)
@@ -1,3 +1,22 @@
+2013-03-11  Alec Flett  <alecflett@chromium.org>
+
+        IndexedDB: Protect against key prefix overflows
+        https://bugs.webkit.org/show_bug.cgi?id=111138
+
+        Reviewed by Tony Chang.
+
+        This reworks the boundary checking for all databaseId,
+        objectStoreId, and indexId, including negative and
+        zero-based ids. All entrypoints into IDBLevelDBCoding
+        are protected with explicit checks and all internal
+        uses of KeyPrefix are protected with ASSERTs in the
+        various constructors.
+
+        Tests: WebKit unit tests IDBBackingStoreTest.cpp in WebKit/chromium
+
+        * Modules/indexeddb/IDBBackingStore.h: Make all public methods boolean-based for errors.
+        * Modules/indexeddb/IDBLevelDBCoding.h: Add methods for checking databaseId, objectStoreId, and indexId.
+
 2013-03-11  Philip Rogers  <pdr@google.com>
 
         Replace static_cast<SVGStyledElement> with toSVGStyledElement()
index 0abcfed..1cae047 100644 (file)
@@ -636,9 +636,11 @@ static bool checkObjectStoreAndMetaDataType(const LevelDBIterator* it, const Vec
 }
 
 // FIXME: This should do some error handling rather than plowing ahead when bad data is encountered.
-void IDBBackingStore::getObjectStores(int64_t databaseId, IDBDatabaseMetadata::ObjectStoreMap* objectStores)
+bool IDBBackingStore::getObjectStores(int64_t databaseId, IDBDatabaseMetadata::ObjectStoreMap* objectStores)
 {
     IDB_TRACE("IDBBackingStore::getObjectStores");
+    if (!KeyPrefix::isValidDatabaseId(databaseId))
+        return false;
     const Vector<char> startKey = ObjectStoreMetaDataKey::encode(databaseId, 1, 0);
     const Vector<char> stopKey = ObjectStoreMetaDataKey::encodeMaxKey(databaseId);
 
@@ -724,9 +726,11 @@ void IDBBackingStore::getObjectStores(int64_t databaseId, IDBDatabaseMetadata::O
         }
 
         IDBObjectStoreMetadata metadata(objectStoreName, objectStoreId, keyPath, autoIncrement, maxIndexId);
-        getIndexes(databaseId, objectStoreId, &metadata.indexes);
+        if (!getIndexes(databaseId, objectStoreId, &metadata.indexes))
+            return false;
         objectStores->set(objectStoreId, metadata);
     }
+    return true;
 }
 
 WARN_UNUSED_RETURN static bool setMaxObjectStoreId(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId)
@@ -750,6 +754,8 @@ WARN_UNUSED_RETURN static bool setMaxObjectStoreId(LevelDBTransaction* transacti
 bool IDBBackingStore::createObjectStore(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, const String& name, const IDBKeyPath& keyPath, bool autoIncrement)
 {
     IDB_TRACE("IDBBackingStore::createObjectStore");
+    if (!KeyPrefix::validIds(databaseId, objectStoreId))
+        return false;
     LevelDBTransaction* levelDBTransaction = IDBBackingStore::Transaction::levelDBTransactionFrom(transaction);
     if (!setMaxObjectStoreId(levelDBTransaction, databaseId, objectStoreId))
         return false;
@@ -779,6 +785,8 @@ bool IDBBackingStore::createObjectStore(IDBBackingStore::Transaction* transactio
 bool IDBBackingStore::deleteObjectStore(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId)
 {
     IDB_TRACE("IDBBackingStore::deleteObjectStore");
+    if (!KeyPrefix::validIds(databaseId, objectStoreId))
+        return false;
     LevelDBTransaction* levelDBTransaction = IDBBackingStore::Transaction::levelDBTransactionFrom(transaction);
 
     String objectStoreName;
@@ -800,13 +808,14 @@ bool IDBBackingStore::deleteObjectStore(IDBBackingStore::Transaction* transactio
     deleteRange(levelDBTransaction, IndexFreeListKey::encode(databaseId, objectStoreId, 0), IndexFreeListKey::encodeMaxKey(databaseId, objectStoreId));
     deleteRange(levelDBTransaction, IndexMetaDataKey::encode(databaseId, objectStoreId, 0, 0), IndexMetaDataKey::encodeMaxKey(databaseId, objectStoreId));
 
-    clearObjectStore(transaction, databaseId, objectStoreId);
-    return true;
+    return clearObjectStore(transaction, databaseId, objectStoreId);
 }
 
 bool IDBBackingStore::getRecord(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, const IDBKey& key, Vector<char>& record)
 {
     IDB_TRACE("IDBBackingStore::getRecord");
+    if (!KeyPrefix::validIds(databaseId, objectStoreId))
+        return false;
     LevelDBTransaction* levelDBTransaction = IDBBackingStore::Transaction::levelDBTransactionFrom(transaction);
 
     const Vector<char> leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key);
@@ -863,7 +872,10 @@ WARN_UNUSED_RETURN static bool getNewVersionNumber(LevelDBTransaction* transacti
 bool IDBBackingStore::putRecord(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, const IDBKey& key, PassRefPtr<SharedBuffer> prpValue, RecordIdentifier* recordIdentifier)
 {
     IDB_TRACE("IDBBackingStore::putRecord");
+    if (!KeyPrefix::validIds(databaseId, objectStoreId))
+        return false;
     ASSERT(key.isValid());
+
     LevelDBTransaction* levelDBTransaction = IDBBackingStore::Transaction::levelDBTransactionFrom(transaction);
     int64_t version = -1;
     bool ok = getNewVersionNumber(levelDBTransaction, databaseId, objectStoreId, version);
@@ -887,19 +899,24 @@ bool IDBBackingStore::putRecord(IDBBackingStore::Transaction* transaction, int64
     return true;
 }
 
-void IDBBackingStore::clearObjectStore(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId)
+bool IDBBackingStore::clearObjectStore(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId)
 {
     IDB_TRACE("IDBBackingStore::clearObjectStore");
+    if (!KeyPrefix::validIds(databaseId, objectStoreId))
+        return false;
     LevelDBTransaction* levelDBTransaction = IDBBackingStore::Transaction::levelDBTransactionFrom(transaction);
-    const Vector<char> startKey = KeyPrefix(databaseId, objectStoreId, 0).encode();
-    const Vector<char> stopKey = KeyPrefix(databaseId, objectStoreId + 1, 0).encode();
+    const Vector<char> startKey = KeyPrefix(databaseId, objectStoreId).encode();
+    const Vector<char> stopKey = KeyPrefix(databaseId, objectStoreId + 1).encode();
 
     deleteRange(levelDBTransaction, startKey, stopKey);
+    return true;
 }
 
-void IDBBackingStore::deleteRecord(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, const RecordIdentifier& recordIdentifier)
+bool IDBBackingStore::deleteRecord(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, const RecordIdentifier& recordIdentifier)
 {
     IDB_TRACE("IDBBackingStore::deleteRecord");
+    if (!KeyPrefix::validIds(databaseId, objectStoreId))
+        return false;
     LevelDBTransaction* levelDBTransaction = IDBBackingStore::Transaction::levelDBTransactionFrom(transaction);
 
     const Vector<char> objectStoreDataKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, recordIdentifier.primaryKey());
@@ -907,11 +924,14 @@ void IDBBackingStore::deleteRecord(IDBBackingStore::Transaction* transaction, in
 
     const Vector<char> existsEntryKey = ExistsEntryKey::encode(databaseId, objectStoreId, recordIdentifier.primaryKey());
     levelDBTransaction->remove(existsEntryKey);
+    return true;
 }
 
 
 bool IDBBackingStore::getKeyGeneratorCurrentNumber(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t& keyGeneratorCurrentNumber)
 {
+    if (!KeyPrefix::validIds(databaseId, objectStoreId))
+        return false;
     LevelDBTransaction* levelDBTransaction = IDBBackingStore::Transaction::levelDBTransactionFrom(transaction);
 
     const Vector<char> keyGeneratorCurrentNumberKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, ObjectStoreMetaDataKey::KeyGeneratorCurrentNumber);
@@ -960,6 +980,8 @@ bool IDBBackingStore::getKeyGeneratorCurrentNumber(IDBBackingStore::Transaction*
 
 bool IDBBackingStore::maybeUpdateKeyGeneratorCurrentNumber(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t newNumber, bool checkCurrent)
 {
+    if (!KeyPrefix::validIds(databaseId, objectStoreId))
+        return false;
     LevelDBTransaction* levelDBTransaction = IDBBackingStore::Transaction::levelDBTransactionFrom(transaction);
 
     if (checkCurrent) {
@@ -979,6 +1001,8 @@ bool IDBBackingStore::maybeUpdateKeyGeneratorCurrentNumber(IDBBackingStore::Tran
 bool IDBBackingStore::keyExistsInObjectStore(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, const IDBKey& key, RecordIdentifier* foundRecordIdentifier, bool& found)
 {
     IDB_TRACE("IDBBackingStore::keyExistsInObjectStore");
+    if (!KeyPrefix::validIds(databaseId, objectStoreId))
+        return false;
     found = false;
     LevelDBTransaction* levelDBTransaction = IDBBackingStore::Transaction::levelDBTransactionFrom(transaction);
     const Vector<char> leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key);
@@ -1017,9 +1041,11 @@ static bool checkIndexAndMetaDataKey(const LevelDBIterator* it, const Vector<cha
 
 
 // FIXME: This should do some error handling rather than plowing ahead when bad data is encountered.
-void IDBBackingStore::getIndexes(int64_t databaseId, int64_t objectStoreId, IDBObjectStoreMetadata::IndexMap* indexes)
+bool IDBBackingStore::getIndexes(int64_t databaseId, int64_t objectStoreId, IDBObjectStoreMetadata::IndexMap* indexes)
 {
     IDB_TRACE("IDBBackingStore::getIndexes");
+    if (!KeyPrefix::validIds(databaseId, objectStoreId))
+        return false;
     const Vector<char> startKey = IndexMetaDataKey::encode(databaseId, objectStoreId, 0, 0);
     const Vector<char> stopKey = IndexMetaDataKey::encode(databaseId, objectStoreId + 1, 0, 0);
 
@@ -1068,6 +1094,7 @@ void IDBBackingStore::getIndexes(int64_t databaseId, int64_t objectStoreId, IDBO
 
         indexes->set(indexId, IDBIndexMetadata(indexName, indexId, keyPath, indexUnique, indexMultiEntry));
     }
+    return true;
 }
 
 WARN_UNUSED_RETURN static bool setMaxIndexId(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId)
@@ -1095,6 +1122,8 @@ WARN_UNUSED_RETURN static bool setMaxIndexId(LevelDBTransaction* transaction, in
 bool IDBBackingStore::createIndex(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath& keyPath, bool isUnique, bool isMultiEntry)
 {
     IDB_TRACE("IDBBackingStore::createIndex");
+    if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId))
+        return false;
     LevelDBTransaction* levelDBTransaction = IDBBackingStore::Transaction::levelDBTransactionFrom(transaction);
     if (!setMaxIndexId(levelDBTransaction, databaseId, objectStoreId, indexId))
         return false;
@@ -1111,9 +1140,11 @@ bool IDBBackingStore::createIndex(IDBBackingStore::Transaction* transaction, int
     return true;
 }
 
-void IDBBackingStore::deleteIndex(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId)
+bool IDBBackingStore::deleteIndex(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId)
 {
     IDB_TRACE("IDBBackingStore::deleteIndex");
+    if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId))
+        return false;
     LevelDBTransaction* levelDBTransaction = IDBBackingStore::Transaction::levelDBTransactionFrom(transaction);
 
     const Vector<char> indexMetaDataStart = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, 0);
@@ -1123,13 +1154,15 @@ void IDBBackingStore::deleteIndex(IDBBackingStore::Transaction* transaction, int
     const Vector<char> indexDataStart = IndexDataKey::encodeMinKey(databaseId, objectStoreId, indexId);
     const Vector<char> indexDataEnd = IndexDataKey::encodeMaxKey(databaseId, objectStoreId, indexId);
     deleteRange(levelDBTransaction, indexDataStart, indexDataEnd);
+    return true;
 }
 
-void IDBBackingStore::putIndexDataForRecord(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, const RecordIdentifier& recordIdentifier)
+bool IDBBackingStore::putIndexDataForRecord(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, const RecordIdentifier& recordIdentifier)
 {
     IDB_TRACE("IDBBackingStore::putIndexDataForRecord");
     ASSERT(key.isValid());
-    ASSERT(indexId >= MinimumIndexId);
+    if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId))
+        return false;
 
     LevelDBTransaction* levelDBTransaction = IDBBackingStore::Transaction::levelDBTransactionFrom(transaction);
     const Vector<char> indexDataKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, encodeIDBKey(key), recordIdentifier.primaryKey());
@@ -1139,6 +1172,7 @@ void IDBBackingStore::putIndexDataForRecord(IDBBackingStore::Transaction* transa
     data.append(recordIdentifier.primaryKey());
 
     levelDBTransaction->put(indexDataKey, data);
+    return true;
 }
 
 static bool findGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction, const Vector<char>& target, Vector<char>& foundKey)
@@ -1189,6 +1223,8 @@ static bool versionExists(LevelDBTransaction* transaction, int64_t databaseId, i
 bool IDBBackingStore::findKeyInIndex(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, Vector<char>& foundEncodedPrimaryKey, bool& found)
 {
     IDB_TRACE("IDBBackingStore::findKeyInIndex");
+    ASSERT(KeyPrefix::validIds(databaseId, objectStoreId, indexId));
+
     ASSERT(foundEncodedPrimaryKey.isEmpty());
     found = false;
 
@@ -1229,6 +1265,8 @@ bool IDBBackingStore::findKeyInIndex(IDBBackingStore::Transaction* transaction,
 bool IDBBackingStore::getPrimaryKeyViaIndex(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key, RefPtr<IDBKey>& primaryKey)
 {
     IDB_TRACE("IDBBackingStore::getPrimaryKeyViaIndex");
+    if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId))
+        return false;
 
     bool found = false;
     Vector<char> foundEncodedPrimaryKey;
@@ -1248,6 +1286,8 @@ bool IDBBackingStore::getPrimaryKeyViaIndex(IDBBackingStore::Transaction* transa
 bool IDBBackingStore::keyExistsInIndex(IDBBackingStore::Transaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& indexKey, RefPtr<IDBKey>& foundPrimaryKey, bool& exists)
 {
     IDB_TRACE("IDBBackingStore::keyExistsInIndex");
+    if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId))
+        return false;
 
     exists = false;
     Vector<char> foundEncodedPrimaryKey;
@@ -1762,6 +1802,9 @@ bool objectStoreCursorOptions(LevelDBTransaction* transaction, int64_t databaseI
 bool indexCursorOptions(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IndexedDB::CursorDirection direction, IDBBackingStore::Cursor::CursorOptions& cursorOptions)
 {
     ASSERT(transaction);
+    if (!KeyPrefix::validIds(databaseId, objectStoreId, indexId))
+        return false;
+
     bool lowerBound = range && range->lower();
     bool upperBound = range && range->upper();
     cursorOptions.forward = (direction == IndexedDB::CursorNextNoDuplicate || direction == IndexedDB::CursorNext);
index e8ee6b6..9c8e377 100644 (file)
@@ -68,7 +68,7 @@ public:
     virtual bool updateIDBDatabaseIntVersion(IDBBackingStore::Transaction*, int64_t rowId, int64_t intVersion);
     virtual bool deleteDatabase(const String& name);
 
-    void getObjectStores(int64_t databaseId, IDBDatabaseMetadata::ObjectStoreMap*);
+    bool getObjectStores(int64_t databaseId, IDBDatabaseMetadata::ObjectStoreMap*) WARN_UNUSED_RETURN;
     virtual bool createObjectStore(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, const String& name, const IDBKeyPath&, bool autoIncrement);
     virtual bool deleteObjectStore(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId) WARN_UNUSED_RETURN;
 
@@ -89,15 +89,15 @@ public:
 
     virtual bool getRecord(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, const IDBKey&, Vector<char>& record) WARN_UNUSED_RETURN;
     virtual bool putRecord(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, const IDBKey&, PassRefPtr<SharedBuffer> value, RecordIdentifier*) WARN_UNUSED_RETURN;
-    virtual void clearObjectStore(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId);
-    virtual void deleteRecord(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, const RecordIdentifier&);
+    virtual bool clearObjectStore(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId) WARN_UNUSED_RETURN;
+    virtual bool deleteRecord(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, const RecordIdentifier&) WARN_UNUSED_RETURN;
     virtual bool getKeyGeneratorCurrentNumber(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t& currentNumber) WARN_UNUSED_RETURN;
     virtual bool maybeUpdateKeyGeneratorCurrentNumber(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t newState, bool checkCurrent) WARN_UNUSED_RETURN;
     virtual bool keyExistsInObjectStore(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, const IDBKey&, RecordIdentifier* foundRecordIdentifier, bool& found) WARN_UNUSED_RETURN;
 
-    virtual bool createIndex(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath&, bool isUnique, bool isMultiEntry);
-    virtual void deleteIndex(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t indexId);
-    virtual void putIndexDataForRecord(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, const RecordIdentifier&);
+    virtual bool createIndex(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath&, bool isUnique, bool isMultiEntry) WARN_UNUSED_RETURN;
+    virtual bool deleteIndex(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t indexId) WARN_UNUSED_RETURN;
+    virtual bool putIndexDataForRecord(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, const RecordIdentifier&) WARN_UNUSED_RETURN;
     virtual bool getPrimaryKeyViaIndex(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, RefPtr<IDBKey>& primaryKey) WARN_UNUSED_RETURN;
     virtual bool keyExistsInIndex(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& indexKey, RefPtr<IDBKey>& foundPrimaryKey, bool& exists) WARN_UNUSED_RETURN;
 
@@ -178,7 +178,7 @@ protected:
 
 private:
     bool findKeyInIndex(IDBBackingStore::Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, Vector<char>& foundEncodedPrimaryKey, bool& found);
-    void getIndexes(int64_t databaseId, int64_t objectStoreId, IDBObjectStoreMetadata::IndexMap*);
+    bool getIndexes(int64_t databaseId, int64_t objectStoreId, IDBObjectStoreMetadata::IndexMap*) WARN_UNUSED_RETURN;
 
     String m_identifier;
 
index 2c30320..76d8368 100644 (file)
@@ -179,22 +179,22 @@ private:
 
 class DeleteIndexOperation : public IDBTransactionBackendImpl::Operation {
 public:
-    static PassOwnPtr<IDBTransactionBackendImpl::Operation> create(PassRefPtr<IDBBackingStore> backingStore, int64_t objectStoreId, int64_t indexId)
+    static PassOwnPtr<IDBTransactionBackendImpl::Operation> create(PassRefPtr<IDBBackingStore> backingStore, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
     {
-        return adoptPtr(new DeleteIndexOperation(backingStore, objectStoreId, indexId));
+        return adoptPtr(new DeleteIndexOperation(backingStore, objectStoreId, indexMetadata));
     }
     virtual void perform(IDBTransactionBackendImpl*);
 private:
-    DeleteIndexOperation(PassRefPtr<IDBBackingStore> backingStore, int64_t objectStoreId, int64_t indexId)
+    DeleteIndexOperation(PassRefPtr<IDBBackingStore> backingStore, int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
         : m_backingStore(backingStore)
         , m_objectStoreId(objectStoreId)
-        , m_indexId(indexId)
+        , m_indexMetadata(indexMetadata)
     {
     }
 
     const RefPtr<IDBBackingStore> m_backingStore;
     const int64_t m_objectStoreId;
-    const int64_t m_indexId;
+    const IDBIndexMetadata m_indexMetadata;
 };
 
 class CreateIndexAbortOperation : public IDBTransactionBackendImpl::Operation {
@@ -534,10 +534,9 @@ bool IDBDatabaseBackendImpl::openInternal()
     ASSERT_WITH_MESSAGE(success == (m_metadata.id != InvalidId), "success = %s, m_id = %lld", success ? "true" : "false", static_cast<long long>(m_metadata.id));
     if (!ok)
         return false;
-    if (success) {
-        m_backingStore->getObjectStores(m_metadata.id, &m_metadata.objectStores);
-        return true;
-    }
+    if (success)
+        return m_backingStore->getObjectStores(m_metadata.id, &m_metadata.objectStores);
+
     return m_backingStore->createIDBDatabaseMetaData(m_metadata.name, m_metadata.version, m_metadata.intVersion, m_metadata.id);
 }
 
@@ -640,7 +639,7 @@ void IDBDatabaseBackendImpl::deleteIndex(int64_t transactionId, int64_t objectSt
     ASSERT(objectStore.indexes.contains(indexId));
     const IDBIndexMetadata& indexMetadata = objectStore.indexes.get(indexId);
 
-    transaction->scheduleTask(DeleteIndexOperation::create(m_backingStore, objectStoreId, indexId), DeleteIndexAbortOperation::create(this, objectStoreId, indexMetadata));
+    transaction->scheduleTask(DeleteIndexOperation::create(m_backingStore, objectStoreId, indexMetadata), DeleteIndexAbortOperation::create(this, objectStoreId, indexMetadata));
 
     removeIndex(objectStoreId, indexId);
 }
@@ -648,7 +647,11 @@ void IDBDatabaseBackendImpl::deleteIndex(int64_t transactionId, int64_t objectSt
 void DeleteIndexOperation::perform(IDBTransactionBackendImpl* transaction)
 {
     IDB_TRACE("DeleteIndexOperation");
-    m_backingStore->deleteIndex(transaction->backingStoreTransaction(), transaction->database()->id(), m_objectStoreId, m_indexId);
+    bool ok = m_backingStore->deleteIndex(transaction->backingStoreTransaction(), transaction->database()->id(), m_objectStoreId, m_indexMetadata.id);
+    if (!ok) {
+        RefPtr<IDBDatabaseError> error = IDBDatabaseError::create(IDBDatabaseException::UnknownError, String::format("Internal error deleting index '%s'.", m_indexMetadata.name.utf8().data()));
+        transaction->abort(error);
+    }
 }
 
 void DeleteIndexAbortOperation::perform(IDBTransactionBackendImpl* transaction)
@@ -1023,7 +1026,10 @@ void DeleteRangeOperation::perform(IDBTransactionBackendImpl* transaction)
     RefPtr<IDBBackingStore::Cursor> backingStoreCursor = m_backingStore->openObjectStoreCursor(transaction->backingStoreTransaction(), m_databaseId, m_objectStoreId, m_keyRange.get(), IndexedDB::CursorNext);
     if (backingStoreCursor) {
         do {
-            m_backingStore->deleteRecord(transaction->backingStoreTransaction(), m_databaseId, m_objectStoreId, backingStoreCursor->recordIdentifier());
+            if (!m_backingStore->deleteRecord(transaction->backingStoreTransaction(), m_databaseId, m_objectStoreId, backingStoreCursor->recordIdentifier())) {
+                m_callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Error deleting data in range"));
+                return;
+            }
         } while (backingStoreCursor->continueFunction(0));
     }
 
@@ -1044,7 +1050,10 @@ void IDBDatabaseBackendImpl::clear(int64_t transactionId, int64_t objectStoreId,
 void ClearOperation::perform(IDBTransactionBackendImpl* transaction)
 {
     IDB_TRACE("ObjectStoreClearOperation");
-    m_backingStore->clearObjectStore(transaction->backingStoreTransaction(), m_databaseId, m_objectStoreId);
+    if (!m_backingStore->clearObjectStore(transaction->backingStoreTransaction(), m_databaseId, m_objectStoreId)) {
+        m_callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Error clearing object store"));
+        return;
+    }
     m_callbacks->onSuccess();
 }
 
index f8627dc..3fd24cd 100644 (file)
@@ -973,11 +973,66 @@ KeyPrefix::KeyPrefix()
 {
 }
 
+KeyPrefix::KeyPrefix(int64_t databaseId)
+    : m_databaseId(databaseId)
+    , m_objectStoreId(0)
+    , m_indexId(0)
+{
+    ASSERT(KeyPrefix::isValidDatabaseId(databaseId));
+}
+
+KeyPrefix::KeyPrefix(int64_t databaseId, int64_t objectStoreId)
+    : m_databaseId(databaseId)
+    , m_objectStoreId(objectStoreId)
+    , m_indexId(0)
+{
+    ASSERT(KeyPrefix::isValidDatabaseId(databaseId));
+    ASSERT(KeyPrefix::isValidObjectStoreId(objectStoreId));
+}
+
 KeyPrefix::KeyPrefix(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
     : m_databaseId(databaseId)
     , m_objectStoreId(objectStoreId)
     , m_indexId(indexId)
 {
+    ASSERT(KeyPrefix::isValidDatabaseId(databaseId));
+    ASSERT(KeyPrefix::isValidObjectStoreId(objectStoreId));
+    ASSERT(KeyPrefix::isValidIndexId(indexId));
+}
+
+KeyPrefix::KeyPrefix(Type type, int64_t databaseId, int64_t objectStoreId, int64_t indexId)
+    : m_databaseId(databaseId)
+    , m_objectStoreId(objectStoreId)
+    , m_indexId(indexId)
+{
+    ASSERT(type == InvalidType);
+    ASSERT(KeyPrefix::isValidDatabaseId(databaseId));
+    ASSERT(KeyPrefix::isValidObjectStoreId(objectStoreId));
+}
+
+
+KeyPrefix KeyPrefix::createWithSpecialIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
+{
+    ASSERT(KeyPrefix::isValidDatabaseId(databaseId));
+    ASSERT(KeyPrefix::isValidObjectStoreId(objectStoreId));
+    ASSERT(indexId);
+    return KeyPrefix(InvalidType, databaseId, objectStoreId, indexId);
+}
+
+
+bool KeyPrefix::isValidDatabaseId(int64_t databaseId)
+{
+    return (databaseId > 0) && (databaseId < KeyPrefix::kMaxDatabaseId);
+}
+
+bool KeyPrefix::isValidObjectStoreId(int64_t objectStoreId)
+{
+    return (objectStoreId > 0) && (objectStoreId < KeyPrefix::kMaxObjectStoreId);
+}
+
+bool KeyPrefix::isValidIndexId(int64_t indexId)
+{
+    return (indexId >= MinimumIndexId) && (indexId < KeyPrefix::kMaxIndexId);
 }
 
 const char* KeyPrefix::decode(const char* start, const char* limit, KeyPrefix* result)
@@ -1004,22 +1059,33 @@ const char* KeyPrefix::decode(const char* start, const char* limit, KeyPrefix* r
     return start;
 }
 
+Vector<char> KeyPrefix::encodeEmpty()
+{
+    const Vector<char, 4> result(4, 0);
+    ASSERT(encodeInternal(0, 0, 0) == Vector<char>(4, 0));
+    return result;
+}
+
 Vector<char> KeyPrefix::encode() const
 {
     ASSERT(m_databaseId != InvalidId);
     ASSERT(m_objectStoreId != InvalidId);
     ASSERT(m_indexId != InvalidId);
+    return encodeInternal(m_databaseId, m_objectStoreId, m_indexId);
+}
 
-    Vector<char> databaseIdString = encodeInt(m_databaseId);
-    Vector<char> objectStoreIdString = encodeInt(m_objectStoreId);
-    Vector<char> indexIdString = encodeInt(m_indexId);
-
-    ASSERT(databaseIdString.size() <= 8);
-    ASSERT(objectStoreIdString.size() <= 8);
-    ASSERT(indexIdString.size() <= 4);
+Vector<char> KeyPrefix::encodeInternal(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
+{
+    Vector<char> databaseIdString = encodeIntSafely(databaseId, kMaxDatabaseId);
+    Vector<char> objectStoreIdString = encodeIntSafely(objectStoreId, kMaxObjectStoreId);
+    Vector<char> indexIdString = encodeIntSafely(indexId, kMaxIndexId);
 
+    ASSERT(databaseIdString.size() <= kMaxDatabaseIdSizeBytes);
+    ASSERT(objectStoreIdString.size() <= kMaxObjectStoreIdSizeBytes);
+    ASSERT(indexIdString.size() <= kMaxIndexIdSizeBytes);
 
-    unsigned char firstByte = (databaseIdString.size() - 1) << 5 | (objectStoreIdString.size() - 1) << 2 | (indexIdString.size() - 1);
+    unsigned char firstByte = (databaseIdString.size() - 1) << (kMaxObjectStoreIdSizeBits + kMaxIndexIdSizeBits) | (objectStoreIdString.size() - 1) << kMaxIndexIdSizeBits | (indexIdString.size() - 1);
+    COMPILE_ASSERT(kMaxDatabaseIdSizeBits + kMaxObjectStoreIdSizeBits + kMaxIndexIdSizeBits == sizeof(firstByte) * 8, CANT_ENCODE_IDS);
     Vector<char, DefaultInlineBufferSize> ret;
     ret.append(firstByte);
     ret.append(databaseIdString);
@@ -1068,24 +1134,21 @@ KeyPrefix::Type KeyPrefix::type() const
 
 Vector<char> SchemaVersionKey::encode()
 {
-    KeyPrefix prefix(0, 0, 0);
-    Vector<char> ret = prefix.encode();
+    Vector<char> ret = KeyPrefix::encodeEmpty();
     ret.append(encodeByte(SchemaVersionTypeByte));
     return ret;
 }
 
 Vector<char> MaxDatabaseIdKey::encode()
 {
-    KeyPrefix prefix(0, 0, 0);
-    Vector<char> ret = prefix.encode();
+    Vector<char> ret = KeyPrefix::encodeEmpty();
     ret.append(encodeByte(MaxDatabaseIdTypeByte));
     return ret;
 }
 
 Vector<char> DataVersionKey::encode()
 {
-    KeyPrefix prefix(0, 0, 0);
-    Vector<char> ret = prefix.encode();
+    Vector<char> ret = KeyPrefix::encodeEmpty();
     ret.append(encodeByte(DataVersionTypeByte));
     return ret;
 }
@@ -1116,8 +1179,7 @@ const char* DatabaseFreeListKey::decode(const char* start, const char* limit, Da
 
 Vector<char> DatabaseFreeListKey::encode(int64_t databaseId)
 {
-    KeyPrefix prefix(0, 0, 0);
-    Vector<char> ret = prefix.encode();
+    Vector<char> ret = KeyPrefix::encodeEmpty();
     ret.append(encodeByte(DatabaseFreeListTypeByte));
     ret.append(encodeVarInt(databaseId));
     return ret;
@@ -1164,8 +1226,7 @@ const char* DatabaseNameKey::decode(const char* start, const char* limit, Databa
 
 Vector<char> DatabaseNameKey::encode(const String& origin, const String& databaseName)
 {
-    KeyPrefix prefix(0, 0, 0);
-    Vector<char> ret = prefix.encode();
+    Vector<char> ret = KeyPrefix::encodeEmpty();
     ret.append(encodeByte(DatabaseNameTypeByte));
     ret.append(encodeStringWithLength(origin));
     ret.append(encodeStringWithLength(databaseName));
@@ -1192,7 +1253,7 @@ int DatabaseNameKey::compare(const DatabaseNameKey& other)
 
 Vector<char> DatabaseMetaDataKey::encode(int64_t databaseId, MetaDataType metaDataType)
 {
-    KeyPrefix prefix(databaseId, 0, 0);
+    KeyPrefix prefix(databaseId);
     Vector<char> ret = prefix.encode();
     ret.append(encodeByte(metaDataType));
     return ret;
@@ -1231,7 +1292,7 @@ const char* ObjectStoreMetaDataKey::decode(const char* start, const char* limit,
 
 Vector<char> ObjectStoreMetaDataKey::encode(int64_t databaseId, int64_t objectStoreId, unsigned char metaDataType)
 {
-    KeyPrefix prefix(databaseId, 0, 0);
+    KeyPrefix prefix(databaseId);
     Vector<char> ret = prefix.encode();
     ret.append(encodeByte(ObjectStoreMetaDataTypeByte));
     ret.append(encodeVarInt(objectStoreId));
@@ -1306,7 +1367,7 @@ const char* IndexMetaDataKey::decode(const char* start, const char* limit, Index
 
 Vector<char> IndexMetaDataKey::encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId, unsigned char metaDataType)
 {
-    KeyPrefix prefix(databaseId, 0, 0);
+    KeyPrefix prefix(databaseId);
     Vector<char> ret = prefix.encode();
     ret.append(encodeByte(IndexMetaDataTypeByte));
     ret.append(encodeVarInt(objectStoreId));
@@ -1369,7 +1430,7 @@ const char* ObjectStoreFreeListKey::decode(const char* start, const char* limit,
 
 Vector<char> ObjectStoreFreeListKey::encode(int64_t databaseId, int64_t objectStoreId)
 {
-    KeyPrefix prefix(databaseId, 0, 0);
+    KeyPrefix prefix(databaseId);
     Vector<char> ret = prefix.encode();
     ret.append(encodeByte(ObjectStoreFreeListTypeByte));
     ret.append(encodeVarInt(objectStoreId));
@@ -1426,7 +1487,7 @@ const char* IndexFreeListKey::decode(const char* start, const char* limit, Index
 
 Vector<char> IndexFreeListKey::encode(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
 {
-    KeyPrefix prefix(databaseId, 0, 0);
+    KeyPrefix prefix(databaseId);
     Vector<char> ret = prefix.encode();
     ret.append(encodeByte(IndexFreeListTypeByte));
     ret.append(encodeVarInt(objectStoreId));
@@ -1482,7 +1543,7 @@ const char* ObjectStoreNamesKey::decode(const char* start, const char* limit, Ob
 
 Vector<char> ObjectStoreNamesKey::encode(int64_t databaseId, const String& objectStoreName)
 {
-    KeyPrefix prefix(databaseId, 0, 0);
+    KeyPrefix prefix(databaseId);
     Vector<char> ret = prefix.encode();
     ret.append(encodeByte(ObjectStoreNamesTypeByte));
     ret.append(encodeStringWithLength(objectStoreName));
@@ -1525,7 +1586,7 @@ const char* IndexNamesKey::decode(const char* start, const char* limit, IndexNam
 
 Vector<char> IndexNamesKey::encode(int64_t databaseId, int64_t objectStoreId, const String& indexName)
 {
-    KeyPrefix prefix(databaseId, 0, 0);
+    KeyPrefix prefix(databaseId);
     Vector<char> ret = prefix.encode();
     ret.append(encodeByte(IndexNamesKeyTypeByte));
     ret.append(encodeVarInt(objectStoreId));
@@ -1557,7 +1618,7 @@ const char* ObjectStoreDataKey::decode(const char* start, const char* end, Objec
 
 Vector<char> ObjectStoreDataKey::encode(int64_t databaseId, int64_t objectStoreId, const Vector<char> encodedUserKey)
 {
-    KeyPrefix prefix(databaseId, objectStoreId, SpecialIndexNumber);
+    KeyPrefix prefix(KeyPrefix::createWithSpecialIndex(databaseId, objectStoreId, SpecialIndexNumber));
     Vector<char> ret = prefix.encode();
     ret.append(encodedUserKey);
 
@@ -1599,7 +1660,7 @@ const char* ExistsEntryKey::decode(const char* start, const char* end, ExistsEnt
 
 Vector<char> ExistsEntryKey::encode(int64_t databaseId, int64_t objectStoreId, const Vector<char>& encodedKey)
 {
-    KeyPrefix prefix(databaseId, objectStoreId, SpecialIndexNumber);
+    KeyPrefix prefix(KeyPrefix::createWithSpecialIndex(databaseId, objectStoreId, SpecialIndexNumber));
     Vector<char> ret = prefix.encode();
     ret.append(encodedKey);
     return ret;
index 024cdea..ddcd79a 100644 (file)
@@ -54,6 +54,11 @@ Vector<char> minIDBKey();
 Vector<char> encodeBool(bool);
 bool decodeBool(const char* begin, const char* end);
 Vector<char> encodeInt(int64_t);
+inline Vector<char> encodeIntSafely(int64_t nParam, size_t max)
+{
+    ASSERT(static_cast<size_t>(nParam) <= max);
+    return encodeInt(nParam);
+}
 int64_t decodeInt(const char* begin, const char* end);
 Vector<char> encodeVarInt(int64_t);
 const char* decodeVarInt(const char* p, const char* limit, int64_t& foundInt);
@@ -77,10 +82,14 @@ int compare(const LevelDBSlice&, const LevelDBSlice&, bool indexKeys = false);
 class KeyPrefix {
 public:
     KeyPrefix();
+    explicit KeyPrefix(int64_t databaseId);
+    KeyPrefix(int64_t databaseId, int64_t objectStoreId);
     KeyPrefix(int64_t databaseId, int64_t objectStoreId, int64_t indexId);
+    static KeyPrefix createWithSpecialIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId);
 
     static const char* decode(const char* start, const char* limit, KeyPrefix* result);
     Vector<char> encode() const;
+    static Vector<char> encodeEmpty();
     int compare(const KeyPrefix& other) const;
 
     enum Type {
@@ -92,6 +101,34 @@ public:
         InvalidType
     };
 
+    static const size_t kMaxDatabaseIdSizeBits = 3;
+    static const size_t kMaxObjectStoreIdSizeBits = 3;
+    static const size_t kMaxIndexIdSizeBits = 2;
+
+    static const size_t kMaxDatabaseIdSizeBytes = 1ULL << kMaxDatabaseIdSizeBits; // 8
+    static const size_t kMaxObjectStoreIdSizeBytes = 1ULL << kMaxObjectStoreIdSizeBits; // 8
+    static const size_t kMaxIndexIdSizeBytes = 1ULL << kMaxIndexIdSizeBits; // 4
+
+    static const size_t kMaxDatabaseIdBits = kMaxDatabaseIdSizeBytes * 8 - 1; // 63
+    static const size_t kMaxObjectStoreIdBits = kMaxObjectStoreIdSizeBytes * 8 - 1; // 63
+    static const size_t kMaxIndexIdBits = kMaxIndexIdSizeBytes * 8 - 1; // 31
+
+    static const int64_t kMaxDatabaseId = (1ULL << kMaxDatabaseIdBits) - 1; // max signed int64_t
+    static const int64_t kMaxObjectStoreId = (1ULL << kMaxObjectStoreIdBits) - 1; // max signed int64_t
+    static const int64_t kMaxIndexId = (1ULL << kMaxIndexIdBits) - 1; // max signed int32_t
+
+    static bool isValidDatabaseId(int64_t databaseId);
+    static bool isValidObjectStoreId(int64_t indexId);
+    static bool isValidIndexId(int64_t indexId);
+    static bool validIds(int64_t databaseId, int64_t objectStoreId, int64_t indexId)
+    {
+        return isValidDatabaseId(databaseId) && isValidObjectStoreId(objectStoreId) && isValidIndexId(indexId);
+    }
+    static bool validIds(int64_t databaseId, int64_t objectStoreId)
+    {
+        return isValidDatabaseId(databaseId) && isValidObjectStoreId(objectStoreId);
+    }
+
     Type type() const;
 
     int64_t m_databaseId;
@@ -99,6 +136,11 @@ public:
     int64_t m_indexId;
 
     static const int64_t InvalidId = -1;
+
+private:
+    static Vector<char> encodeInternal(int64_t databaseId, int64_t objectStoreId, int64_t indexId);
+    // Special constructor for createWithSpecialIndex()
+    KeyPrefix(Type, int64_t databaseId, int64_t objectStoreId, int64_t indexId);
 };
 
 class SchemaVersionKey {
index 4fc8b17..5c5ed0c 100644 (file)
@@ -64,7 +64,9 @@ void IDBObjectStoreBackendImpl::IndexWriter::writeIndexKeys(const IDBBackingStor
 {
     int64_t indexId = m_indexMetadata.id;
     for (size_t i = 0; i < m_indexKeys.size(); ++i) {
-        backingStore.putIndexDataForRecord(transaction, databaseId, objectStoreId, indexId, *(m_indexKeys)[i].get(), recordIdentifier);
+        bool ok = backingStore.putIndexDataForRecord(transaction, databaseId, objectStoreId, indexId, *(m_indexKeys)[i].get(), recordIdentifier);
+        // This should have already been verified as a valid write during verifyIndexKeys.
+        ASSERT_UNUSED(ok, ok);
     }
 }
 
index 283b39f..f52965b 100644 (file)
@@ -1,3 +1,12 @@
+2013-03-11  Alec Flett  <alecflett@chromium.org>
+
+        IndexedDB: Protect against key prefix overflows
+        https://bugs.webkit.org/show_bug.cgi?id=111138
+
+        Reviewed by Tony Chang.
+
+        Add tests for invalid indexIds in basic get/put operations.
+
 2013-03-11  Xiyuan Xia  <xiyuan@chromium.org>
 
         [Chromium] chromium/linux breaks expectation of select popup background due to bad UA css rules
index 5302e12..0197a15 100644 (file)
@@ -28,6 +28,7 @@
 #include "IDBBackingStore.h"
 
 #include "IDBFactoryBackendImpl.h"
+#include "IDBLevelDBCoding.h"
 #include "SharedBuffer.h"
 
 #include <gtest/gtest.h>
@@ -36,6 +37,7 @@
 #if ENABLE(INDEXED_DATABASE)
 
 using namespace WebCore;
+using IDBLevelDBCoding::KeyPrefix;
 
 namespace {
 
@@ -53,10 +55,13 @@ public:
         // useful keys and values during tests
         const char rawValue1[] = "value1";
         const char rawValue2[] = "value2";
+        const char rawValue3[] = "value3";
         m_value1.append(rawValue1, sizeof(rawValue1));
         m_value2.append(rawValue2, sizeof(rawValue2));
+        m_value3.append(rawValue3, sizeof(rawValue3));
         m_key1 = IDBKey::createNumber(99);
         m_key2 = IDBKey::createString("key2");
+        m_key3 = IDBKey::createString("key3");
     }
 
 protected:
@@ -65,8 +70,10 @@ protected:
     // Sample keys and values that are consistent.
     RefPtr<IDBKey> m_key1;
     RefPtr<IDBKey> m_key2;
+    RefPtr<IDBKey> m_key3;
     Vector<char> m_value1;
     Vector<char> m_value2;
+    Vector<char> m_value3;
 };
 
 TEST_F(IDBBackingStoreTest, PutGetConsistency)
@@ -96,12 +103,26 @@ TEST_F(IDBBackingStoreTest, HighIds)
 {
     const int64_t highDatabaseId = 1ULL << 35;
     const int64_t highObjectStoreId = 1ULL << 39;
+    // indexIds are capped at 32 bits for storage purposes.
+    const int64_t highIndexId = 1ULL << 29;
+
+    const int64_t invalidHighIndexId = 1ULL << 37;
+
+    const RefPtr<IDBKey> indexKey = m_key2;
+    Vector<char> indexKeyRaw = IDBLevelDBCoding::encodeIDBKey(*indexKey);
     {
         IDBBackingStore::Transaction transaction1(m_backingStore.get());
         transaction1.begin();
         IDBBackingStore::RecordIdentifier record;
         bool ok = m_backingStore->putRecord(&transaction1, highDatabaseId, highObjectStoreId, *m_key1.get(), SharedBuffer::create(m_value1.data(), m_value1.size()), &record);
         EXPECT_TRUE(ok);
+
+        ok = m_backingStore->putIndexDataForRecord(&transaction1, highDatabaseId, highObjectStoreId, invalidHighIndexId, *indexKey, record);
+        EXPECT_FALSE(ok);
+
+        ok = m_backingStore->putIndexDataForRecord(&transaction1, highDatabaseId, highObjectStoreId, highIndexId, *indexKey, record);
+        EXPECT_TRUE(ok);
+
         ok = transaction1.commit();
         EXPECT_TRUE(ok);
     }
@@ -112,12 +133,69 @@ TEST_F(IDBBackingStoreTest, HighIds)
         Vector<char> resultValue;
         bool ok = m_backingStore->getRecord(&transaction2, highDatabaseId, highObjectStoreId, *m_key1.get(), resultValue);
         EXPECT_TRUE(ok);
+        EXPECT_EQ(m_value1, resultValue);
+
+        RefPtr<IDBKey> newPrimaryKey;
+        ok = m_backingStore->getPrimaryKeyViaIndex(&transaction2, highDatabaseId, highObjectStoreId, invalidHighIndexId, *indexKey, newPrimaryKey);
+        EXPECT_FALSE(ok);
+
+        ok = m_backingStore->getPrimaryKeyViaIndex(&transaction2, highDatabaseId, highObjectStoreId, highIndexId, *indexKey, newPrimaryKey);
+        EXPECT_TRUE(ok);
+        EXPECT_TRUE(newPrimaryKey->isEqual(m_key1.get()));
+
         ok = transaction2.commit();
         EXPECT_TRUE(ok);
-        EXPECT_EQ(m_value1, resultValue);
     }
 }
 
+// Make sure that other invalid ids do not crash.
+TEST_F(IDBBackingStoreTest, InvalidIds)
+{
+    // valid ids for use when testing invalid ids
+    const int64_t databaseId = 1;
+    const int64_t objectStoreId = 1;
+    const int64_t indexId = IDBLevelDBCoding::MinimumIndexId;
+    const int64_t invalidLowIndexId = 19; // indexIds must be > IDBLevelDBCoding::MinimumIndexId
+
+    const RefPtr<SharedBuffer> value = SharedBuffer::create(m_value1.data(), m_value1.size());
+    Vector<char> resultValue;
+
+    IDBBackingStore::Transaction transaction1(m_backingStore.get());
+    transaction1.begin();
+
+    IDBBackingStore::RecordIdentifier record;
+    bool ok = m_backingStore->putRecord(&transaction1, databaseId, KeyPrefix::InvalidId, *m_key1.get(), value, &record);
+    EXPECT_FALSE(ok);
+    ok = m_backingStore->putRecord(&transaction1, databaseId, 0, *m_key1.get(), value, &record);
+    EXPECT_FALSE(ok);
+    ok = m_backingStore->putRecord(&transaction1, KeyPrefix::InvalidId, objectStoreId, *m_key1.get(), value, &record);
+    EXPECT_FALSE(ok);
+    ok = m_backingStore->putRecord(&transaction1, 0, objectStoreId, *m_key1.get(), value, &record);
+    EXPECT_FALSE(ok);
+
+    ok = m_backingStore->getRecord(&transaction1, databaseId, KeyPrefix::InvalidId, *m_key1.get(), resultValue);
+    EXPECT_FALSE(ok);
+    ok = m_backingStore->getRecord(&transaction1, databaseId, 0, *m_key1.get(), resultValue);
+    EXPECT_FALSE(ok);
+    ok = m_backingStore->getRecord(&transaction1, KeyPrefix::InvalidId, objectStoreId, *m_key1.get(), resultValue);
+    EXPECT_FALSE(ok);
+    ok = m_backingStore->getRecord(&transaction1, 0, objectStoreId, *m_key1.get(), resultValue);
+    EXPECT_FALSE(ok);
+
+    RefPtr<IDBKey> newPrimaryKey;
+    ok = m_backingStore->getPrimaryKeyViaIndex(&transaction1, databaseId, objectStoreId, KeyPrefix::InvalidId, *m_key1, newPrimaryKey);
+    EXPECT_FALSE(ok);
+    ok = m_backingStore->getPrimaryKeyViaIndex(&transaction1, databaseId, objectStoreId, invalidLowIndexId, *m_key1, newPrimaryKey);
+    EXPECT_FALSE(ok);
+    ok = m_backingStore->getPrimaryKeyViaIndex(&transaction1, databaseId, objectStoreId, 0, *m_key1, newPrimaryKey);
+    EXPECT_FALSE(ok);
+
+    ok = m_backingStore->getPrimaryKeyViaIndex(&transaction1, KeyPrefix::InvalidId, objectStoreId, indexId, *m_key1, newPrimaryKey);
+    EXPECT_FALSE(ok);
+    ok = m_backingStore->getPrimaryKeyViaIndex(&transaction1, databaseId, KeyPrefix::InvalidId, indexId, *m_key1, newPrimaryKey);
+    EXPECT_FALSE(ok);
+}
+
 TEST_F(IDBBackingStoreTest, CreateDatabase)
 {
     const String databaseName("db1");
@@ -166,7 +244,8 @@ TEST_F(IDBBackingStoreTest, CreateDatabase)
         EXPECT_EQ(intVersion, database.intVersion);
         EXPECT_EQ(databaseId, database.id);
 
-        m_backingStore->getObjectStores(database.id, &database.objectStores);
+        ok = m_backingStore->getObjectStores(database.id, &database.objectStores);
+        EXPECT_TRUE(ok);
 
         EXPECT_EQ(1, database.objectStores.size());
         IDBObjectStoreMetadata objectStore = database.objectStores.get(objectStoreId);
index eefc648..a47b593 100644 (file)
@@ -41,15 +41,15 @@ public:
 
     virtual bool createObjectStore(Transaction*, int64_t databaseId, int64_t objectStoreId, const String& name, const IDBKeyPath&, bool autoIncrement) OVERRIDE { return false; };
 
-    virtual void clearObjectStore(Transaction*, int64_t databaseId, int64_t objectStoreId) OVERRIDE { }
-    virtual void deleteRecord(Transaction*, int64_t databaseId, int64_t objectStoreId, const RecordIdentifier&) OVERRIDE { }
+    virtual bool clearObjectStore(Transaction*, int64_t databaseId, int64_t objectStoreId) OVERRIDE { return false; }
+    virtual bool deleteRecord(Transaction*, int64_t databaseId, int64_t objectStoreId, const RecordIdentifier&) OVERRIDE { return false; }
     virtual bool getKeyGeneratorCurrentNumber(Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t& currentNumber) OVERRIDE { return true; }
     virtual bool maybeUpdateKeyGeneratorCurrentNumber(Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t newNumber, bool checkCurrent) OVERRIDE { return true; }
     virtual bool keyExistsInObjectStore(Transaction*, int64_t databaseId, int64_t objectStoreId, const IDBKey&, RecordIdentifier* foundRecordIdentifier, bool& found) OVERRIDE { return true; }
 
     virtual bool createIndex(Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath&, bool isUnique, bool isMultiEntry) OVERRIDE { return false; };
-    virtual void deleteIndex(Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t indexId) OVERRIDE { }
-    virtual void putIndexDataForRecord(Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, const RecordIdentifier&) OVERRIDE { }
+    virtual bool deleteIndex(Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t indexId) OVERRIDE { return false; }
+    virtual bool putIndexDataForRecord(Transaction*, int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey&, const RecordIdentifier&) OVERRIDE { return false; }
 
     virtual PassRefPtr<Cursor> openObjectStoreKeyCursor(Transaction*, int64_t databaseId, int64_t objectStoreId, const IDBKeyRange*, IndexedDB::CursorDirection) OVERRIDE { return PassRefPtr<Cursor>(); }
     virtual PassRefPtr<Cursor> openObjectStoreCursor(Transaction*, int64_t databaseId, int64_t objectStoreId, const IDBKeyRange*, IndexedDB::CursorDirection) OVERRIDE { return PassRefPtr<Cursor>(); }
index eb2cc2a..f4e45c7 100644 (file)
@@ -101,7 +101,7 @@ TEST(IDBLevelDBCodingTest, DecodeByte)
         EXPECT_EQ(v.data() + v.size(), p);
     }
 }
-   
+
 TEST(IDBLevelDBCodingTest, EncodeBool)
 {
     {
@@ -711,7 +711,7 @@ TEST(IDBLevelDBCodingTest, ComparisonTest)
     keys.append(IndexDataKey::encode(1, 1, 30, maxIDBKey(), maxIDBKey(), 1));
     keys.append(IndexDataKey::encode(1, 1, 31, minIDBKey(), minIDBKey(), 0));
     keys.append(IndexDataKey::encode(1, 2, 30, minIDBKey(), minIDBKey(), 0));
-    keys.append(IndexDataKey::encodeMaxKey(1, 2, INT32_MAX));
+    keys.append(IndexDataKey::encodeMaxKey(1, 2, INT32_MAX - 1));
 
     for (size_t i = 0; i < keys.size(); ++i) {
         const LevelDBSlice keyA(keys[i]);