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   skip_closing_sequence_ = true;
188 }
189 
ReportOutstandingBlobs(bool blobs_outstanding)190 void IndexedDBOriginState::ReportOutstandingBlobs(bool blobs_outstanding) {
191   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
192   has_blobs_outstanding_ = blobs_outstanding;
193   MaybeStartClosing();
194 }
195 
StopPersistingForIncognito()196 void IndexedDBOriginState::StopPersistingForIncognito() {
197   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
198   persist_for_incognito_ = false;
199   MaybeStartClosing();
200 }
201 
202 std::tuple<IndexedDBOriginState::RunTasksResult, leveldb::Status>
RunTasks()203 IndexedDBOriginState::RunTasks() {
204   task_run_scheduled_ = false;
205   running_tasks_ = true;
206   leveldb::Status status;
207   for (auto db_it = databases_.begin(); db_it != databases_.end();) {
208     IndexedDBDatabase& db = *db_it->second;
209 
210     IndexedDBDatabase::RunTasksResult tasks_result;
211     std::tie(tasks_result, status) = db.RunTasks();
212     switch (tasks_result) {
213       case IndexedDBDatabase::RunTasksResult::kDone:
214         ++db_it;
215         continue;
216       case IndexedDBDatabase::RunTasksResult::kError:
217         running_tasks_ = false;
218         return std::make_tuple(RunTasksResult::kError, status);
219       case IndexedDBDatabase::RunTasksResult::kCanBeDestroyed:
220         db_it = databases_.erase(db_it);
221         break;
222     }
223   }
224   running_tasks_ = false;
225   if (CanCloseFactory() && closing_stage_ == ClosingState::kClosed)
226     return std::make_tuple(RunTasksResult::kCanBeDestroyed, leveldb::Status::OK());
227   return std::make_tuple(RunTasksResult::kDone, leveldb::Status::OK());
228 }
229 
AddDatabase(const base::string16 & name,std::unique_ptr<IndexedDBDatabase> database)230 IndexedDBDatabase* IndexedDBOriginState::AddDatabase(
231     const base::string16& name,
232     std::unique_ptr<IndexedDBDatabase> database) {
233   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
234   DCHECK(!base::Contains(databases_, name));
235   return databases_.emplace(name, std::move(database)).first->second.get();
236 }
237 
CreateHandle()238 IndexedDBOriginStateHandle IndexedDBOriginState::CreateHandle() {
239   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
240   ++open_handles_;
241   if (closing_stage_ != ClosingState::kNotClosing) {
242     closing_stage_ = ClosingState::kNotClosing;
243     close_timer_.AbandonAndStop();
244     if (pre_close_task_queue_) {
245       pre_close_task_queue_->StopForNewConnection();
246       pre_close_task_queue_.reset();
247     }
248   }
249   return IndexedDBOriginStateHandle(weak_factory_.GetWeakPtr());
250 }
251 
OnHandleDestruction()252 void IndexedDBOriginState::OnHandleDestruction() {
253   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
254   DCHECK_GT(open_handles_, 0ll);
255   --open_handles_;
256   MaybeStartClosing();
257 }
258 
CanCloseFactory()259 bool IndexedDBOriginState::CanCloseFactory() {
260   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
261   DCHECK_GE(open_handles_, 0);
262   return !has_blobs_outstanding_ && open_handles_ <= 0 &&
263          !persist_for_incognito_;
264 }
265 
MaybeStartClosing()266 void IndexedDBOriginState::MaybeStartClosing() {
267   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
268   if (!IsClosing() && CanCloseFactory())
269     StartClosing();
270 }
271 
StartClosing()272 void IndexedDBOriginState::StartClosing() {
273   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
274   DCHECK(CanCloseFactory());
275   DCHECK(!IsClosing());
276 
277   if (skip_closing_sequence_ ||
278       base::CommandLine::ForCurrentProcess()->HasSwitch(
279           kIDBCloseImmediatelySwitch)) {
280     closing_stage_ = ClosingState::kClosed;
281     close_timer_.AbandonAndStop();
282     pre_close_task_queue_.reset();
283     notify_tasks_callback_.Run();
284     return;
285   }
286 
287   // Start a timer to close the backing store, unless something else opens it
288   // in the mean time.
289   DCHECK(!close_timer_.IsRunning());
290   closing_stage_ = ClosingState::kPreCloseGracePeriod;
291   close_timer_.Start(
292       FROM_HERE, base::TimeDelta::FromSeconds(kBackingStoreGracePeriodSeconds),
293       base::BindOnce(
294           [](base::WeakPtr<IndexedDBOriginState> factory) {
295             if (!factory ||
296                 factory->closing_stage_ != ClosingState::kPreCloseGracePeriod)
297               return;
298             factory->StartPreCloseTasks();
299           },
300           weak_factory_.GetWeakPtr()));
301 }
302 
StartPreCloseTasks()303 void IndexedDBOriginState::StartPreCloseTasks() {
304   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
305   DCHECK(closing_stage_ == ClosingState::kPreCloseGracePeriod);
306   closing_stage_ = ClosingState::kRunningPreCloseTasks;
307 
308   // The callback will run on all early returns in this function.
309   base::ScopedClosureRunner maybe_close_backing_store_runner(base::BindOnce(
310       [](base::WeakPtr<IndexedDBOriginState> factory) {
311         if (!factory ||
312             factory->closing_stage_ != ClosingState::kRunningPreCloseTasks)
313           return;
314         factory->closing_stage_ = ClosingState::kClosed;
315         factory->pre_close_task_queue_.reset();
316         factory->close_timer_.AbandonAndStop();
317         factory->notify_tasks_callback_.Run();
318       },
319       weak_factory_.GetWeakPtr()));
320 
321   std::list<std::unique_ptr<IndexedDBPreCloseTaskQueue::PreCloseTask>> tasks;
322 
323   if (ShouldRunTombstoneSweeper()) {
324     tasks.push_back(std::make_unique<IndexedDBTombstoneSweeper>(
325         kTombstoneSweeperRoundIterations, kTombstoneSweeperMaxIterations,
326         backing_store_->db()->db()));
327   }
328 
329   if (ShouldRunCompaction()) {
330     tasks.push_back(
331         std::make_unique<IndexedDBCompactionTask>(backing_store_->db()->db()));
332   }
333 
334   if (!tasks.empty()) {
335     pre_close_task_queue_ = std::make_unique<IndexedDBPreCloseTaskQueue>(
336         std::move(tasks), maybe_close_backing_store_runner.Release(),
337         base::TimeDelta::FromSeconds(kRunningPreCloseTasksMaxRunPeriodSeconds),
338         std::make_unique<base::OneShotTimer>());
339     pre_close_task_queue_->Start(
340         base::BindOnce(&IndexedDBBackingStore::GetCompleteMetadata,
341                        base::Unretained(backing_store_.get())));
342   }
343 }
344 
ShouldRunTombstoneSweeper()345 bool IndexedDBOriginState::ShouldRunTombstoneSweeper() {
346   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
347   base::Time now = clock_->Now();
348   // Check that the last sweep hasn't run too recently.
349   if (*earliest_global_sweep_time_ > now)
350     return false;
351 
352   base::Time origin_earliest_sweep;
353   leveldb::Status s = indexed_db::GetEarliestSweepTime(backing_store_->db(),
354                                                        &origin_earliest_sweep);
355   // TODO(dmurph): Log this or report to UMA.
356   if (!s.ok() && !s.IsNotFound())
357     return false;
358 
359   // This origin hasn't been swept too recently.
360   if (origin_earliest_sweep > now)
361     return false;
362 
363   // A sweep will happen now, so reset the sweep timers.
364   *earliest_global_sweep_time_ = GenerateNextGlobalSweepTime(now);
365   std::unique_ptr<LevelDBDirectTransaction> txn =
366       transactional_leveldb_factory_->CreateLevelDBDirectTransaction(
367           backing_store_->db());
368   s = indexed_db::SetEarliestSweepTime(txn.get(),
369                                        GenerateNextOriginSweepTime(now));
370   // TODO(dmurph): Log this or report to UMA.
371   if (!s.ok())
372     return false;
373   s = txn->Commit();
374 
375   // TODO(dmurph): Log this or report to UMA.
376   if (!s.ok())
377     return false;
378   return true;
379 }
380 
ShouldRunCompaction()381 bool IndexedDBOriginState::ShouldRunCompaction() {
382   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
383   return base::FeatureList::IsEnabled(kCompactIDBOnClose);
384 }
385 
386 }  // namespace content
387