1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "content/browser/indexed_db/indexed_db_origin_state.h"
6 
7 #include <list>
8 #include <utility>
9 #include <vector>
10 
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/feature_list.h"
14 #include "base/rand_util.h"
15 #include "base/stl_util.h"
16 #include "base/synchronization/waitable_event.h"
17 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
18 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_factory.h"
19 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_transaction.h"
20 #include "content/browser/indexed_db/indexed_db_active_blob_registry.h"
21 #include "content/browser/indexed_db/indexed_db_backing_store.h"
22 #include "content/browser/indexed_db/indexed_db_class_factory.h"
23 #include "content/browser/indexed_db/indexed_db_compaction_task.h"
24 #include "content/browser/indexed_db/indexed_db_connection.h"
25 #include "content/browser/indexed_db/indexed_db_database.h"
26 #include "content/browser/indexed_db/indexed_db_factory_impl.h"
27 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
28 #include "content/browser/indexed_db/indexed_db_leveldb_operations.h"
29 #include "content/browser/indexed_db/indexed_db_pre_close_task_queue.h"
30 #include "content/browser/indexed_db/indexed_db_tombstone_sweeper.h"
31 #include "content/browser/indexed_db/indexed_db_transaction.h"
32 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
33 
34 namespace content {
35 namespace {
36 // Time after the last connection to a database is closed and when we destroy
37 // the backing store.
38 const int64_t kBackingStoreGracePeriodSeconds = 2;
39 // Total time we let pre-close tasks run.
40 const int64_t kRunningPreCloseTasksMaxRunPeriodSeconds = 60;
41 // The number of iterations for every 'round' of the tombstone sweeper.
42 const int kTombstoneSweeperRoundIterations = 1000;
43 // The maximum total iterations for the tombstone sweeper.
44 const int kTombstoneSweeperMaxIterations = 10 * 1000 * 1000;
45 
46 constexpr const base::TimeDelta kMinEarliestOriginSweepFromNow =
47     base::TimeDelta::FromDays(1);
48 static_assert(kMinEarliestOriginSweepFromNow <
49                   IndexedDBOriginState::kMaxEarliestOriginSweepFromNow,
50               "Min < Max");
51 
52 constexpr const base::TimeDelta kMinEarliestGlobalSweepFromNow =
53     base::TimeDelta::FromMinutes(5);
54 static_assert(kMinEarliestGlobalSweepFromNow <
55                   IndexedDBOriginState::kMaxEarliestGlobalSweepFromNow,
56               "Min < Max");
57 
GenerateNextOriginSweepTime(base::Time now)58 base::Time GenerateNextOriginSweepTime(base::Time now) {
59   uint64_t range =
60       IndexedDBOriginState::kMaxEarliestOriginSweepFromNow.InMilliseconds() -
61       kMinEarliestOriginSweepFromNow.InMilliseconds();
62   int64_t rand_millis = kMinEarliestOriginSweepFromNow.InMilliseconds() +
63                         static_cast<int64_t>(base::RandGenerator(range));
64   return now + base::TimeDelta::FromMilliseconds(rand_millis);
65 }
66 
GenerateNextGlobalSweepTime(base::Time now)67 base::Time GenerateNextGlobalSweepTime(base::Time now) {
68   uint64_t range =
69       IndexedDBOriginState::kMaxEarliestGlobalSweepFromNow.InMilliseconds() -
70       kMinEarliestGlobalSweepFromNow.InMilliseconds();
71   int64_t rand_millis = kMinEarliestGlobalSweepFromNow.InMilliseconds() +
72                         static_cast<int64_t>(base::RandGenerator(range));
73   return now + base::TimeDelta::FromMilliseconds(rand_millis);
74 }
75 
76 }  // namespace
77 
78 const base::Feature kCompactIDBOnClose{"CompactIndexedDBOnClose",
79                                        base::FEATURE_ENABLED_BY_DEFAULT};
80 
81 constexpr const base::TimeDelta
82     IndexedDBOriginState::kMaxEarliestGlobalSweepFromNow;
83 constexpr const base::TimeDelta
84     IndexedDBOriginState::kMaxEarliestOriginSweepFromNow;
85 
IndexedDBOriginState(url::Origin origin,bool persist_for_incognito,base::Clock * clock,TransactionalLevelDBFactory * transactional_leveldb_factory,base::Time * earliest_global_sweep_time,std::unique_ptr<DisjointRangeLockManager> lock_manager,TasksAvailableCallback notify_tasks_callback,TearDownCallback tear_down_callback,std::unique_ptr<IndexedDBBackingStore> backing_store)86 IndexedDBOriginState::IndexedDBOriginState(
87     url::Origin origin,
88     bool persist_for_incognito,
89     base::Clock* clock,
90     TransactionalLevelDBFactory* transactional_leveldb_factory,
91     base::Time* earliest_global_sweep_time,
92     std::unique_ptr<DisjointRangeLockManager> lock_manager,
93     TasksAvailableCallback notify_tasks_callback,
94     TearDownCallback tear_down_callback,
95     std::unique_ptr<IndexedDBBackingStore> backing_store)
96     : origin_(std::move(origin)),
97       persist_for_incognito_(persist_for_incognito),
98       clock_(clock),
99       transactional_leveldb_factory_(transactional_leveldb_factory),
100       earliest_global_sweep_time_(earliest_global_sweep_time),
101       lock_manager_(std::move(lock_manager)),
102       backing_store_(std::move(backing_store)),
103       notify_tasks_callback_(std::move(notify_tasks_callback)),
104       tear_down_callback_(std::move(tear_down_callback)) {
105   DCHECK(clock_);
106   DCHECK(earliest_global_sweep_time_);
107   if (*earliest_global_sweep_time_ == base::Time())
108     *earliest_global_sweep_time_ = GenerateNextGlobalSweepTime(clock_->Now());
109 }
110 
~IndexedDBOriginState()111 IndexedDBOriginState::~IndexedDBOriginState() {
112   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
113   if (!backing_store_)
114     return;
115   if (backing_store_->IsBlobCleanupPending())
116     backing_store_->ForceRunBlobCleanup();
117 
118   base::WaitableEvent leveldb_destruct_event;
119   backing_store_->db()->leveldb_state()->RequestDestruction(
120       &leveldb_destruct_event);
121   backing_store_.reset();
122   leveldb_destruct_event.Wait();
123 }
124 
AbortAllTransactions(bool compact)125 void IndexedDBOriginState::AbortAllTransactions(bool compact) {
126   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
127 
128   // Because finishing all transactions could cause a database to be destructed
129   // (which would mutate the database_ map), save the keys beforehand and use
130   // those.
131   std::vector<base::string16> origins;
132   origins.reserve(databases_.size());
133   for (const auto& pair : databases_) {
134     origins.push_back(pair.first);
135   }
136 
137   base::WeakPtr<IndexedDBOriginState> weak_ptr = AsWeakPtr();
138   for (const base::string16& origin : origins) {
139     auto it = databases_.find(origin);
140     if (it == databases_.end())
141       continue;
142 
143     // Calling FinishAllTransactions can destruct the IndexedDBConnection &
144     // modify the IndexedDBDatabase::connection() list. To prevent UAFs, start
145     // by taking a WeakPtr of all connections, and then iterate that list.
146     std::vector<base::WeakPtr<IndexedDBConnection>> weak_connections;
147     weak_connections.reserve(it->second->connections().size());
148     for (IndexedDBConnection* connection : it->second->connections())
149       weak_connections.push_back(connection->GetWeakPtr());
150 
151     for (base::WeakPtr<IndexedDBConnection> connection : weak_connections) {
152       if (connection) {
153         leveldb::Status status =
154             connection->AbortAllTransactions(IndexedDBDatabaseError(
155                 blink::mojom::IDBException::kUnknownError,
156                 "Aborting all transactions for the origin."));
157         if (!status.ok()) {
158           // This call should delete this object.
159           tear_down_callback().Run(status);
160           return;
161         }
162       }
163     }
164   }
165 
166   if (compact)
167     backing_store_->Compact();
168 }
169 
ForceClose()170 void IndexedDBOriginState::ForceClose() {
171   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
172   // To avoid re-entry, the |db_destruction_weak_factory_| is invalidated so
173   // that of the deletions closures returned by CreateDatabaseDeleteClosure will
174   // no-op. This allows force closing all of the databases without having the
175   // map mutate. Afterwards the map is manually deleted.
176   IndexedDBOriginStateHandle handle = CreateHandle();
177   for (const auto& pair : databases_) {
178     // Note: We purposefully ignore the result here as force close needs to
179     // continue tearing things down anyways.
180     pair.second->ForceCloseAndRunTasks();
181   }
182   databases_.clear();
183   if (has_blobs_outstanding_) {
184     backing_store_->active_blob_registry()->ForceShutdown();
185     has_blobs_outstanding_ = false;
186   }
187 
188   // Don't run the preclosing tasks after a ForceClose, whether or not we've
189   // started them.  Compaction in particular can run long and cannot be
190   // interrupted, so it can cause shutdown hangs.
191   close_timer_.AbandonAndStop();
192   if (pre_close_task_queue_) {
193     pre_close_task_queue_->Stop(
194         IndexedDBPreCloseTaskQueue::StopReason::FORCE_CLOSE);
195     pre_close_task_queue_.reset();
196   }
197   skip_closing_sequence_ = true;
198 }
199 
ReportOutstandingBlobs(bool blobs_outstanding)200 void IndexedDBOriginState::ReportOutstandingBlobs(bool blobs_outstanding) {
201   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
202   has_blobs_outstanding_ = blobs_outstanding;
203   MaybeStartClosing();
204 }
205 
StopPersistingForIncognito()206 void IndexedDBOriginState::StopPersistingForIncognito() {
207   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
208   persist_for_incognito_ = false;
209   MaybeStartClosing();
210 }
211 
212 std::tuple<IndexedDBOriginState::RunTasksResult, leveldb::Status>
RunTasks()213 IndexedDBOriginState::RunTasks() {
214   task_run_scheduled_ = false;
215   running_tasks_ = true;
216   leveldb::Status status;
217   for (auto db_it = databases_.begin(); db_it != databases_.end();) {
218     IndexedDBDatabase& db = *db_it->second;
219 
220     IndexedDBDatabase::RunTasksResult tasks_result;
221     std::tie(tasks_result, status) = db.RunTasks();
222     switch (tasks_result) {
223       case IndexedDBDatabase::RunTasksResult::kDone:
224         ++db_it;
225         continue;
226       case IndexedDBDatabase::RunTasksResult::kError:
227         running_tasks_ = false;
228         return {RunTasksResult::kError, status};
229       case IndexedDBDatabase::RunTasksResult::kCanBeDestroyed:
230         db_it = databases_.erase(db_it);
231         break;
232     }
233   }
234   running_tasks_ = false;
235   if (CanCloseFactory() && closing_stage_ == ClosingState::kClosed)
236     return {RunTasksResult::kCanBeDestroyed, leveldb::Status::OK()};
237   return {RunTasksResult::kDone, leveldb::Status::OK()};
238 }
239 
AddDatabase(const base::string16 & name,std::unique_ptr<IndexedDBDatabase> database)240 IndexedDBDatabase* IndexedDBOriginState::AddDatabase(
241     const base::string16& name,
242     std::unique_ptr<IndexedDBDatabase> database) {
243   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
244   DCHECK(!base::Contains(databases_, name));
245   return databases_.emplace(name, std::move(database)).first->second.get();
246 }
247 
CreateHandle()248 IndexedDBOriginStateHandle IndexedDBOriginState::CreateHandle() {
249   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
250   ++open_handles_;
251   if (closing_stage_ != ClosingState::kNotClosing) {
252     closing_stage_ = ClosingState::kNotClosing;
253     close_timer_.AbandonAndStop();
254     if (pre_close_task_queue_) {
255       pre_close_task_queue_->Stop(
256           IndexedDBPreCloseTaskQueue::StopReason::NEW_CONNECTION);
257       pre_close_task_queue_.reset();
258     }
259   }
260   return IndexedDBOriginStateHandle(weak_factory_.GetWeakPtr());
261 }
262 
OnHandleDestruction()263 void IndexedDBOriginState::OnHandleDestruction() {
264   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
265   DCHECK_GT(open_handles_, 0ll);
266   --open_handles_;
267   MaybeStartClosing();
268 }
269 
CanCloseFactory()270 bool IndexedDBOriginState::CanCloseFactory() {
271   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
272   DCHECK_GE(open_handles_, 0);
273   return !has_blobs_outstanding_ && open_handles_ <= 0 &&
274          !persist_for_incognito_;
275 }
276 
MaybeStartClosing()277 void IndexedDBOriginState::MaybeStartClosing() {
278   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
279   if (!IsClosing() && CanCloseFactory())
280     StartClosing();
281 }
282 
StartClosing()283 void IndexedDBOriginState::StartClosing() {
284   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
285   DCHECK(CanCloseFactory());
286   DCHECK(!IsClosing());
287 
288   if (skip_closing_sequence_ ||
289       base::CommandLine::ForCurrentProcess()->HasSwitch(
290           kIDBCloseImmediatelySwitch)) {
291     closing_stage_ = ClosingState::kClosed;
292     close_timer_.AbandonAndStop();
293     pre_close_task_queue_.reset();
294     notify_tasks_callback_.Run();
295     return;
296   }
297 
298   // Start a timer to close the backing store, unless something else opens it
299   // in the mean time.
300   DCHECK(!close_timer_.IsRunning());
301   closing_stage_ = ClosingState::kPreCloseGracePeriod;
302   close_timer_.Start(
303       FROM_HERE, base::TimeDelta::FromSeconds(kBackingStoreGracePeriodSeconds),
304       base::BindOnce(
305           [](base::WeakPtr<IndexedDBOriginState> factory) {
306             if (!factory ||
307                 factory->closing_stage_ != ClosingState::kPreCloseGracePeriod)
308               return;
309             factory->StartPreCloseTasks();
310           },
311           weak_factory_.GetWeakPtr()));
312 }
313 
StartPreCloseTasks()314 void IndexedDBOriginState::StartPreCloseTasks() {
315   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
316   DCHECK(closing_stage_ == ClosingState::kPreCloseGracePeriod);
317   closing_stage_ = ClosingState::kRunningPreCloseTasks;
318 
319   // The callback will run on all early returns in this function.
320   base::ScopedClosureRunner maybe_close_backing_store_runner(base::BindOnce(
321       [](base::WeakPtr<IndexedDBOriginState> factory) {
322         if (!factory ||
323             factory->closing_stage_ != ClosingState::kRunningPreCloseTasks)
324           return;
325         factory->closing_stage_ = ClosingState::kClosed;
326         factory->pre_close_task_queue_.reset();
327         factory->close_timer_.AbandonAndStop();
328         factory->notify_tasks_callback_.Run();
329       },
330       weak_factory_.GetWeakPtr()));
331 
332   std::list<std::unique_ptr<IndexedDBPreCloseTaskQueue::PreCloseTask>> tasks;
333 
334   if (ShouldRunTombstoneSweeper()) {
335     tasks.push_back(std::make_unique<IndexedDBTombstoneSweeper>(
336         kTombstoneSweeperRoundIterations, kTombstoneSweeperMaxIterations,
337         backing_store_->db()->db()));
338   }
339 
340   if (ShouldRunCompaction()) {
341     tasks.push_back(
342         std::make_unique<IndexedDBCompactionTask>(backing_store_->db()->db()));
343   }
344 
345   if (!tasks.empty()) {
346     pre_close_task_queue_ = std::make_unique<IndexedDBPreCloseTaskQueue>(
347         std::move(tasks), maybe_close_backing_store_runner.Release(),
348         base::TimeDelta::FromSeconds(kRunningPreCloseTasksMaxRunPeriodSeconds),
349         std::make_unique<base::OneShotTimer>());
350     pre_close_task_queue_->Start(
351         base::BindOnce(&IndexedDBBackingStore::GetCompleteMetadata,
352                        base::Unretained(backing_store_.get())));
353   }
354 }
355 
ShouldRunTombstoneSweeper()356 bool IndexedDBOriginState::ShouldRunTombstoneSweeper() {
357   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
358   base::Time now = clock_->Now();
359   // Check that the last sweep hasn't run too recently.
360   if (*earliest_global_sweep_time_ > now)
361     return false;
362 
363   base::Time origin_earliest_sweep;
364   leveldb::Status s = indexed_db::GetEarliestSweepTime(backing_store_->db(),
365                                                        &origin_earliest_sweep);
366   // TODO(dmurph): Log this or report to UMA.
367   if (!s.ok() && !s.IsNotFound())
368     return false;
369 
370   // This origin hasn't been swept too recently.
371   if (origin_earliest_sweep > now)
372     return false;
373 
374   // A sweep will happen now, so reset the sweep timers.
375   *earliest_global_sweep_time_ = GenerateNextGlobalSweepTime(now);
376   std::unique_ptr<LevelDBDirectTransaction> txn =
377       transactional_leveldb_factory_->CreateLevelDBDirectTransaction(
378           backing_store_->db());
379   s = indexed_db::SetEarliestSweepTime(txn.get(),
380                                        GenerateNextOriginSweepTime(now));
381   // TODO(dmurph): Log this or report to UMA.
382   if (!s.ok())
383     return false;
384   s = txn->Commit();
385 
386   // TODO(dmurph): Log this or report to UMA.
387   if (!s.ok())
388     return false;
389   return true;
390 }
391 
ShouldRunCompaction()392 bool IndexedDBOriginState::ShouldRunCompaction() {
393   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
394   return base::FeatureList::IsEnabled(kCompactIDBOnClose);
395 }
396 
397 }  // namespace content
398