Changeset 247219 in webkit
- Timestamp:
- Jul 8, 2019 11:38:32 AM (5 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r247218 r247219 1 2019-07-08 Chris Dumez <cdumez@apple.com> 2 3 Fix thread safety issue in Database::scheduleTransactionCallback() 4 https://bugs.webkit.org/show_bug.cgi?id=199557 5 6 Reviewed by Alex Christensen. 7 8 I am working on adding threading assertions to WeakPtr and found a potentially 9 unsafe call to makeWeakPtr() on a Document from Database::scheduleTransactionCallback() 10 via Document::postTask(), on a background database thread. Document is a main thread 11 object and we should therefore not be interacting with it from a background thread. 12 13 For clarity, this patch also switches the webdatabase code to use Document instead 14 of ScriptExecution as type since it is only exposed to Window contexts, not workers. 15 16 * Modules/webdatabase/Database.cpp: 17 (WebCore::Database::Database): 18 (WebCore::Database::~Database): 19 (WebCore::Database::runTransaction): 20 (WebCore::Database::scheduleTransactionCallback): 21 (WebCore::Database::logErrorMessage): 22 (WebCore::Database::securityOrigin): 23 (WebCore::Database::didExceedQuota): 24 * Modules/webdatabase/Database.h: 25 (WebCore::Database::document): 26 * Modules/webdatabase/DatabaseContext.cpp: 27 (WebCore::DatabaseContext::DatabaseContext): 28 * Modules/webdatabase/DatabaseContext.h: 29 * Modules/webdatabase/DatabaseManager.cpp: 30 (WebCore::DatabaseManager::databaseContext): 31 (WebCore::logOpenDatabaseError): 32 (WebCore::DatabaseManager::openDatabaseBackend): 33 (WebCore::DatabaseManager::tryToOpenDatabaseBackend): 34 (WebCore::DatabaseManager::openDatabase): 35 (WebCore::DatabaseManager::hasOpenDatabases): 36 (WebCore::DatabaseManager::stopDatabases): 37 (WebCore::DatabaseManager::logErrorMessage): 38 * Modules/webdatabase/DatabaseManager.h: 39 * Modules/webdatabase/SQLStatement.cpp: 40 (WebCore::SQLStatement::SQLStatement): 41 * Modules/webdatabase/SQLTransaction.cpp: 42 (WebCore::SQLTransaction::SQLTransaction): 43 * inspector/InspectorInstrumentation.h: 44 (WebCore::InspectorInstrumentation::didOpenDatabase): 45 * inspector/agents/InspectorDatabaseAgent.cpp: 46 (WebCore::InspectorDatabaseAgent::executeSQL): 47 1 48 2019-07-08 Chris Dumez <cdumez@apple.com> 2 49 -
trunk/Source/WebCore/Modules/webdatabase/Database.cpp
r243219 r247219 194 194 195 195 Database::Database(DatabaseContext& context, const String& name, const String& expectedVersion, const String& displayName, unsigned long long estimatedSize) 196 : m_ scriptExecutionContext(*context.scriptExecutionContext())197 , m_contextThreadSecurityOrigin(m_ scriptExecutionContext->securityOrigin()->isolatedCopy())198 , m_databaseThreadSecurityOrigin(m_ scriptExecutionContext->securityOrigin()->isolatedCopy())196 : m_document(*context.document()) 197 , m_contextThreadSecurityOrigin(m_document->securityOrigin().isolatedCopy()) 198 , m_databaseThreadSecurityOrigin(m_document->securityOrigin().isolatedCopy()) 199 199 , m_databaseContext(context) 200 200 , m_name((name.isNull() ? emptyString() : name).isolatedCopy()) … … 202 202 , m_displayName(displayName.isolatedCopy()) 203 203 , m_estimatedSize(estimatedSize) 204 , m_filename(DatabaseManager::singleton().fullPathForDatabase( *m_scriptExecutionContext->securityOrigin(), m_name))204 , m_filename(DatabaseManager::singleton().fullPathForDatabase(m_document->securityOrigin(), m_name)) 205 205 , m_databaseAuthorizer(DatabaseAuthorizer::create(unqualifiedInfoTableName)) 206 206 { … … 227 227 Database::~Database() 228 228 { 229 // The reference to the ScriptExecutionContext needs to be cleared on the JavaScript thread. If we're on that thread already, we can just let the RefPtr's destruction do the dereffing. 230 if (!m_scriptExecutionContext->isContextThread()) { 231 auto passedContext = WTFMove(m_scriptExecutionContext); 232 auto& contextRef = passedContext.get(); 233 contextRef.postTask({ScriptExecutionContext::Task::CleanupTask, [passedContext = WTFMove(passedContext), databaseContext = WTFMove(m_databaseContext)] (ScriptExecutionContext& context) { 234 ASSERT_UNUSED(context, &context == passedContext.ptr()); 235 }}); 236 } 229 // The reference to the Document needs to be cleared on the JavaScript thread. If we're on that thread already, we can just let the RefPtr's destruction do the dereffing. 230 if (!isMainThread()) 231 callOnMainThread([document = WTFMove(m_document), databaseContext = WTFMove(m_databaseContext)] { }); 237 232 238 233 // SQLite is "multi-thread safe", but each database handle can only be used … … 693 688 if (!m_isTransactionQueueEnabled) { 694 689 if (errorCallback) { 695 RefPtr<SQLTransactionErrorCallback> errorCallbackProtector = WTFMove(errorCallback); 696 m_scriptExecutionContext->postTask([errorCallbackProtector](ScriptExecutionContext&) { 697 errorCallbackProtector->handleEvent(SQLError::create(SQLError::UNKNOWN_ERR, "database has been closed")); 690 callOnMainThread([errorCallback = makeRef(*errorCallback)]() { 691 errorCallback->handleEvent(SQLError::create(SQLError::UNKNOWN_ERR, "database has been closed")); 698 692 }); 699 693 } … … 708 702 void Database::scheduleTransactionCallback(SQLTransaction* transaction) 709 703 { 710 RefPtr<SQLTransaction> transactionProtector(transaction); 711 m_scriptExecutionContext->postTask([transactionProtector] (ScriptExecutionContext&) { 712 transactionProtector->performPendingCallback(); 704 callOnMainThread([transaction = makeRefPtr(transaction)] { 705 transaction->performPendingCallback(); 713 706 }); 714 707 } … … 758 751 void Database::logErrorMessage(const String& message) 759 752 { 760 m_ scriptExecutionContext->addConsoleMessage(MessageSource::Storage, MessageLevel::Error, message);753 m_document->addConsoleMessage(MessageSource::Storage, MessageLevel::Error, message); 761 754 } 762 755 … … 780 773 SecurityOriginData Database::securityOrigin() 781 774 { 782 if ( m_scriptExecutionContext->isContextThread())775 if (isMainThread()) 783 776 return m_contextThreadSecurityOrigin->data(); 784 777 if (databaseThread().getThread() == &Thread::current()) … … 799 792 bool Database::didExceedQuota() 800 793 { 801 ASSERT( databaseContext().scriptExecutionContext()->isContextThread());794 ASSERT(isMainThread()); 802 795 auto& tracker = DatabaseTracker::singleton(); 803 796 auto oldQuota = tracker.quota(securityOrigin()); -
trunk/Source/WebCore/Modules/webdatabase/Database.h
r243219 r247219 40 40 class DatabaseDetails; 41 41 class DatabaseThread; 42 class ScriptExecutionContext;42 class Document; 43 43 class SecurityOrigin; 44 44 class SQLTransaction; … … 106 106 DatabaseContext& databaseContext() { return m_databaseContext; } 107 107 DatabaseThread& databaseThread(); 108 ScriptExecutionContext& scriptExecutionContext() { return m_scriptExecutionContext; }108 Document& document() { return m_document; } 109 109 void logErrorMessage(const String& message); 110 110 … … 148 148 #endif 149 149 150 Ref< ScriptExecutionContext> m_scriptExecutionContext;150 Ref<Document> m_document; 151 151 Ref<SecurityOrigin> m_contextThreadSecurityOrigin; 152 152 Ref<SecurityOrigin> m_databaseThreadSecurityOrigin; -
trunk/Source/WebCore/Modules/webdatabase/DatabaseContext.cpp
r229979 r247219 95 95 96 96 97 DatabaseContext::DatabaseContext( ScriptExecutionContext& context)98 : ActiveDOMObject( &context)97 DatabaseContext::DatabaseContext(Document& document) 98 : ActiveDOMObject(document) 99 99 { 100 100 // ActiveDOMObject expects this to be called to set internal flags. 101 101 suspendIfNeeded(); 102 102 103 ASSERT(! context.databaseContext());104 context.setDatabaseContext(this);103 ASSERT(!document.databaseContext()); 104 document.setDatabaseContext(this); 105 105 } 106 106 -
trunk/Source/WebCore/Modules/webdatabase/DatabaseContext.h
r237266 r247219 61 61 void databaseExceededQuota(const String& name, DatabaseDetails); 62 62 63 using ActiveDOMObject::scriptExecutionContext;63 Document* document() const { return downcast<Document>(ActiveDOMObject::scriptExecutionContext()); } 64 64 const SecurityOriginData& securityOrigin() const; 65 65 … … 67 67 68 68 private: 69 explicit DatabaseContext( ScriptExecutionContext&);69 explicit DatabaseContext(Document&); 70 70 71 71 void stopDatabases() { stopDatabases(nullptr); } -
trunk/Source/WebCore/Modules/webdatabase/DatabaseManager.cpp
r243219 r247219 32 32 #include "DatabaseTask.h" 33 33 #include "DatabaseTracker.h" 34 #include "Document.h" 34 35 #include "InspectorInstrumentation.h" 35 36 #include "Logging.h" 36 37 #include "PlatformStrategies.h" 37 38 #include "ScriptController.h" 38 #include "ScriptExecutionContext.h"39 39 #include "SecurityOrigin.h" 40 40 #include "SecurityOriginData.h" … … 98 98 } 99 99 100 Ref<DatabaseContext> DatabaseManager::databaseContext( ScriptExecutionContext& context)101 { 102 if (auto databaseContext = context.databaseContext())100 Ref<DatabaseContext> DatabaseManager::databaseContext(Document& document) 101 { 102 if (auto databaseContext = document.databaseContext()) 103 103 return *databaseContext; 104 return adoptRef(*new DatabaseContext( context));104 return adoptRef(*new DatabaseContext(document)); 105 105 } 106 106 107 107 #if LOG_DISABLED 108 108 109 static inline void logOpenDatabaseError( ScriptExecutionContext&, const String&)109 static inline void logOpenDatabaseError(Document&, const String&) 110 110 { 111 111 } … … 113 113 #else 114 114 115 static void logOpenDatabaseError( ScriptExecutionContext& context, const String& name)116 { 117 LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.utf8().data(), context.securityOrigin()->toString().utf8().data());115 static void logOpenDatabaseError(Document& document, const String& name) 116 { 117 LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.utf8().data(), document.securityOrigin().toString().utf8().data()); 118 118 } 119 119 120 120 #endif 121 121 122 ExceptionOr<Ref<Database>> DatabaseManager::openDatabaseBackend( ScriptExecutionContext& context, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase)123 { 124 auto backend = tryToOpenDatabaseBackend( context, name, expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, FirstTryToOpenDatabase);122 ExceptionOr<Ref<Database>> DatabaseManager::openDatabaseBackend(Document& document, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase) 123 { 124 auto backend = tryToOpenDatabaseBackend(document, name, expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, FirstTryToOpenDatabase); 125 125 126 126 if (backend.hasException()) { … … 130 130 // one more try after if that is the case. 131 131 { 132 // FIXME: What guarantees context.securityOrigin() is non-null? 133 ProposedDatabase proposedDatabase { *this, *context.securityOrigin(), name, displayName, estimatedSize }; 134 this->databaseContext(context)->databaseExceededQuota(name, proposedDatabase.details()); 132 ProposedDatabase proposedDatabase { *this, document.securityOrigin(), name, displayName, estimatedSize }; 133 this->databaseContext(document)->databaseExceededQuota(name, proposedDatabase.details()); 135 134 } 136 backend = tryToOpenDatabaseBackend( context, name, expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, RetryOpenDatabase);135 backend = tryToOpenDatabaseBackend(document, name, expectedVersion, displayName, estimatedSize, setVersionInNewDatabase, RetryOpenDatabase); 137 136 } 138 137 } … … 140 139 if (backend.hasException()) { 141 140 if (backend.exception().code() == InvalidStateError) 142 logErrorMessage( context, backend.exception().message());141 logErrorMessage(document, backend.exception().message()); 143 142 else 144 logOpenDatabaseError( context, name);143 logOpenDatabaseError(document, name); 145 144 } 146 145 … … 148 147 } 149 148 150 ExceptionOr<Ref<Database>> DatabaseManager::tryToOpenDatabaseBackend( ScriptExecutionContext& scriptContext, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase,149 ExceptionOr<Ref<Database>> DatabaseManager::tryToOpenDatabaseBackend(Document& document, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase, 151 150 OpenAttempt attempt) 152 151 { 153 if (is<Document>(&scriptContext)) { 154 auto* page = downcast<Document>(scriptContext).page(); 155 if (!page || page->usesEphemeralSession()) 156 return Exception { SecurityError }; 157 } 158 159 if (scriptContext.isWorkerGlobalScope()) { 160 ASSERT_NOT_REACHED(); 152 auto* page = document.page(); 153 if (!page || page->usesEphemeralSession()) 161 154 return Exception { SecurityError }; 162 } 163 164 auto backendContext = this->databaseContext(scriptContext); 155 156 auto backendContext = this->databaseContext(document); 165 157 166 158 ExceptionOr<void> preflightResult; … … 199 191 } 200 192 201 ExceptionOr<Ref<Database>> DatabaseManager::openDatabase( ScriptExecutionContext& context, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, RefPtr<DatabaseCallback>&& creationCallback)193 ExceptionOr<Ref<Database>> DatabaseManager::openDatabase(Document& document, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, RefPtr<DatabaseCallback>&& creationCallback) 202 194 { 203 195 ScriptController::initializeThreading(); 204 196 205 197 bool setVersionInNewDatabase = !creationCallback; 206 auto openResult = openDatabaseBackend( context, name, expectedVersion, displayName, estimatedSize, setVersionInNewDatabase);198 auto openResult = openDatabaseBackend(document, name, expectedVersion, displayName, estimatedSize, setVersionInNewDatabase); 207 199 if (openResult.hasException()) 208 200 return openResult.releaseException(); … … 210 202 RefPtr<Database> database = openResult.releaseReturnValue(); 211 203 212 auto databaseContext = this->databaseContext( context);204 auto databaseContext = this->databaseContext(document); 213 205 databaseContext->setHasOpenDatabases(); 214 206 InspectorInstrumentation::didOpenDatabase(*database); … … 217 209 LOG(StorageAPI, "Scheduling DatabaseCreationCallbackTask for database %p\n", database.get()); 218 210 database->setHasPendingCreationEvent(true); 219 database->m_ scriptExecutionContext->postTask([creationCallback, database] (ScriptExecutionContext&) {211 database->m_document->postTask([creationCallback, database] (ScriptExecutionContext&) { 220 212 creationCallback->handleEvent(*database); 221 213 database->setHasPendingCreationEvent(false); … … 226 218 } 227 219 228 bool DatabaseManager::hasOpenDatabases( ScriptExecutionContext& context)229 { 230 auto databaseContext = context.databaseContext();220 bool DatabaseManager::hasOpenDatabases(Document& document) 221 { 222 auto databaseContext = document.databaseContext(); 231 223 return databaseContext && databaseContext->hasOpenDatabases(); 232 224 } 233 225 234 void DatabaseManager::stopDatabases( ScriptExecutionContext& context, DatabaseTaskSynchronizer* synchronizer)235 { 236 auto databaseContext = context.databaseContext();226 void DatabaseManager::stopDatabases(Document& document, DatabaseTaskSynchronizer* synchronizer) 227 { 228 auto databaseContext = document.databaseContext(); 237 229 if (!databaseContext || !databaseContext->stopDatabases(synchronizer)) { 238 230 if (synchronizer) … … 268 260 } 269 261 270 void DatabaseManager::logErrorMessage( ScriptExecutionContext& context, const String& message)271 { 272 context.addConsoleMessage(MessageSource::Storage, MessageLevel::Error, message);262 void DatabaseManager::logErrorMessage(Document& document, const String& message) 263 { 264 document.addConsoleMessage(MessageSource::Storage, MessageLevel::Error, message); 273 265 } 274 266 -
trunk/Source/WebCore/Modules/webdatabase/DatabaseManager.h
r233487 r247219 39 39 class DatabaseManagerClient; 40 40 class DatabaseTaskSynchronizer; 41 class Document; 41 42 class Exception; 42 43 class SecurityOrigin; 43 class ScriptExecutionContext;44 44 struct SecurityOriginData; 45 45 … … 56 56 WEBCORE_EXPORT void setIsAvailable(bool); 57 57 58 // This gets a DatabaseContext for the specified ScriptExecutionContext.58 // This gets a DatabaseContext for the specified Document. 59 59 // If one doesn't already exist, it will create a new one. 60 Ref<DatabaseContext> databaseContext( ScriptExecutionContext&);60 Ref<DatabaseContext> databaseContext(Document&); 61 61 62 ExceptionOr<Ref<Database>> openDatabase( ScriptExecutionContext&, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, RefPtr<DatabaseCallback>&&);62 ExceptionOr<Ref<Database>> openDatabase(Document&, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, RefPtr<DatabaseCallback>&&); 63 63 64 WEBCORE_EXPORT bool hasOpenDatabases( ScriptExecutionContext&);65 void stopDatabases( ScriptExecutionContext&, DatabaseTaskSynchronizer*);64 WEBCORE_EXPORT bool hasOpenDatabases(Document&); 65 void stopDatabases(Document&, DatabaseTaskSynchronizer*); 66 66 67 67 WEBCORE_EXPORT String fullPathForDatabase(SecurityOrigin&, const String& name, bool createIfDoesNotExist = true); … … 76 76 77 77 enum OpenAttempt { FirstTryToOpenDatabase, RetryOpenDatabase }; 78 ExceptionOr<Ref<Database>> openDatabaseBackend( ScriptExecutionContext&, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase);79 ExceptionOr<Ref<Database>> tryToOpenDatabaseBackend( ScriptExecutionContext&, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase, OpenAttempt);78 ExceptionOr<Ref<Database>> openDatabaseBackend(Document&, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase); 79 ExceptionOr<Ref<Database>> tryToOpenDatabaseBackend(Document&, const String& name, const String& expectedVersion, const String& displayName, unsigned estimatedSize, bool setVersionInNewDatabase, OpenAttempt); 80 80 81 81 class ProposedDatabase; … … 83 83 void removeProposedDatabase(ProposedDatabase&); 84 84 85 static void logErrorMessage( ScriptExecutionContext&, const String& message);85 static void logErrorMessage(Document&, const String& message); 86 86 87 87 DatabaseManagerClient* m_client { nullptr }; -
trunk/Source/WebCore/Modules/webdatabase/SQLStatement.cpp
r239535 r247219 30 30 31 31 #include "Database.h" 32 #include "Document.h" 32 33 #include "Logging.h" 33 34 #include "SQLError.h" … … 78 79 : m_statement(statement.isolatedCopy()) 79 80 , m_arguments(WTFMove(arguments)) 80 , m_statementCallbackWrapper(WTFMove(callback), &database. scriptExecutionContext())81 , m_statementErrorCallbackWrapper(WTFMove(errorCallback), &database. scriptExecutionContext())81 , m_statementCallbackWrapper(WTFMove(callback), &database.document()) 82 , m_statementErrorCallbackWrapper(WTFMove(errorCallback), &database.document()) 82 83 , m_permissions(permissions) 83 84 { -
trunk/Source/WebCore/Modules/webdatabase/SQLTransaction.cpp
r242776 r247219 35 35 #include "DatabaseThread.h" 36 36 #include "DatabaseTracker.h" 37 #include "Document.h" 37 38 #include "Logging.h" 38 39 #include "OriginLock.h" … … 60 61 SQLTransaction::SQLTransaction(Ref<Database>&& database, RefPtr<SQLTransactionCallback>&& callback, RefPtr<VoidCallback>&& successCallback, RefPtr<SQLTransactionErrorCallback>&& errorCallback, RefPtr<SQLTransactionWrapper>&& wrapper, bool readOnly) 61 62 : m_database(WTFMove(database)) 62 , m_callbackWrapper(WTFMove(callback), &m_database-> scriptExecutionContext())63 , m_successCallbackWrapper(WTFMove(successCallback), &m_database-> scriptExecutionContext())64 , m_errorCallbackWrapper(WTFMove(errorCallback), &m_database-> scriptExecutionContext())63 , m_callbackWrapper(WTFMove(callback), &m_database->document()) 64 , m_successCallbackWrapper(WTFMove(successCallback), &m_database->document()) 65 , m_errorCallbackWrapper(WTFMove(errorCallback), &m_database->document()) 65 66 , m_wrapper(WTFMove(wrapper)) 66 67 , m_nextStep(&SQLTransaction::acquireLock) -
trunk/Source/WebCore/inspector/InspectorInstrumentation.h
r246876 r247219 1202 1202 { 1203 1203 FAST_RETURN_IF_NO_FRONTENDS(void()); 1204 if (auto* instrumentingAgents = instrumentingAgentsForContext(database. scriptExecutionContext()))1204 if (auto* instrumentingAgents = instrumentingAgentsForContext(database.document())) 1205 1205 didOpenDatabaseImpl(*instrumentingAgents, database); 1206 1206 } -
trunk/Source/WebCore/inspector/agents/InspectorDatabaseAgent.cpp
r243219 r247219 279 279 } 280 280 281 database->transaction(TransactionCallback::create(&database-> scriptExecutionContext(), query, requestCallback.copyRef()),282 TransactionErrorCallback::create(&database-> scriptExecutionContext(), requestCallback.copyRef()),283 TransactionSuccessCallback::create(&database-> scriptExecutionContext()));281 database->transaction(TransactionCallback::create(&database->document(), query, requestCallback.copyRef()), 282 TransactionErrorCallback::create(&database->document(), requestCallback.copyRef()), 283 TransactionSuccessCallback::create(&database->document())); 284 284 } 285 285
Note: See TracChangeset
for help on using the changeset viewer.