Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(429)

Side by Side Diff: sql/connection.cc

Issue 1426743006: [sql] Validate database files before enabling memory-mapping. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Uninit variable warning. Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "sql/connection.h" 5 #include "sql/connection.h"
6 6
7 #include <string.h> 7 #include <string.h>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/debug/dump_without_crashing.h" 10 #include "base/debug/dump_without_crashing.h"
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after
183 183
184 // TODO(shess): NULL in file->pMethods has been observed on android_dbg 184 // TODO(shess): NULL in file->pMethods has been observed on android_dbg
185 // content_unittests, even though it should not be possible. 185 // content_unittests, even though it should not be possible.
186 // http://crbug.com/329982 186 // http://crbug.com/329982
187 if (!*file || !(*file)->pMethods) 187 if (!*file || !(*file)->pMethods)
188 return SQLITE_ERROR; 188 return SQLITE_ERROR;
189 189
190 return rc; 190 return rc;
191 } 191 }
192 192
193 // Convenience to get the sqlite3_file* and the size for the "main" database.
194 int GetSqlite3FileAndSize(sqlite3* db,
195 sqlite3_file** file, sqlite3_int64* db_size) {
196 int rc = GetSqlite3File(db, file);
197 if (rc != SQLITE_OK)
198 return rc;
199
200 return (*file)->pMethods->xFileSize(*file, db_size);
201 }
202
193 // This should match UMA_HISTOGRAM_MEDIUM_TIMES(). 203 // This should match UMA_HISTOGRAM_MEDIUM_TIMES().
194 base::HistogramBase* GetMediumTimeHistogram(const std::string& name) { 204 base::HistogramBase* GetMediumTimeHistogram(const std::string& name) {
195 return base::Histogram::FactoryTimeGet( 205 return base::Histogram::FactoryTimeGet(
196 name, 206 name,
197 base::TimeDelta::FromMilliseconds(10), 207 base::TimeDelta::FromMilliseconds(10),
198 base::TimeDelta::FromMinutes(3), 208 base::TimeDelta::FromMinutes(3),
199 50, 209 50,
200 base::HistogramBase::kUmaTargetedHistogramFlag); 210 base::HistogramBase::kUmaTargetedHistogramFlag);
201 } 211 }
202 212
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after
508 } 518 }
509 519
510 // Use local settings if provided, otherwise use documented defaults. The 520 // Use local settings if provided, otherwise use documented defaults. The
511 // actual results could be fetching via PRAGMA calls. 521 // actual results could be fetching via PRAGMA calls.
512 const int page_size = page_size_ ? page_size_ : 1024; 522 const int page_size = page_size_ ? page_size_ : 1024;
513 sqlite3_int64 preload_size = page_size * (cache_size_ ? cache_size_ : 2000); 523 sqlite3_int64 preload_size = page_size * (cache_size_ ? cache_size_ : 2000);
514 if (preload_size < 1) 524 if (preload_size < 1)
515 return; 525 return;
516 526
517 sqlite3_file* file = NULL; 527 sqlite3_file* file = NULL;
518 int rc = GetSqlite3File(db_, &file); 528 sqlite3_int64 file_size = 0;
529 int rc = GetSqlite3FileAndSize(db_, &file, &file_size);
519 if (rc != SQLITE_OK) 530 if (rc != SQLITE_OK)
520 return; 531 return;
521 532
522 sqlite3_int64 file_size = 0;
523 rc = file->pMethods->xFileSize(file, &file_size);
524 if (rc != SQLITE_OK)
525 return;
526
527 // Don't preload more than the file contains. 533 // Don't preload more than the file contains.
528 if (preload_size > file_size) 534 if (preload_size > file_size)
529 preload_size = file_size; 535 preload_size = file_size;
530 536
531 scoped_ptr<char[]> buf(new char[page_size]); 537 scoped_ptr<char[]> buf(new char[page_size]);
532 for (sqlite3_int64 pos = 0; pos < preload_size; pos += page_size) { 538 for (sqlite3_int64 pos = 0; pos < preload_size; pos += page_size) {
533 rc = file->pMethods->xRead(file, buf.get(), page_size, pos); 539 rc = file->pMethods->xRead(file, buf.get(), page_size, pos);
534 if (rc != SQLITE_OK) 540 if (rc != SQLITE_OK)
535 return; 541 return;
536 } 542 }
(...skipping 311 matching lines...) Expand 10 before | Expand all | Expand 10 after
848 // keep close to the 2000-character size limit for dumping. 854 // keep close to the 2000-character size limit for dumping.
849 const size_t kMaxMessages = 20; 855 const size_t kMaxMessages = 20;
850 for (size_t i = 0; i < kMaxMessages && i < messages.size(); ++i) { 856 for (size_t i = 0; i < kMaxMessages && i < messages.size(); ++i) {
851 base::StringAppendF(&debug_info, "%s\n", messages[i].c_str()); 857 base::StringAppendF(&debug_info, "%s\n", messages[i].c_str());
852 } 858 }
853 } 859 }
854 860
855 return debug_info; 861 return debug_info;
856 } 862 }
857 863
864 size_t Connection::MmapHelper() {
865 AssertIOAllowed();
866
867 // TODO(shess): Using sql::MetaTable seems indicated, but mixing
868 // sql::MetaTable and direct access seems error-prone. It might make sense to
869 // simply integrate sql::MetaTable functionality into sql::Connection.
870
871 #if defined(OS_IOS)
872 // iOS SQLite does not support memory mapping.
873 return 0;
874 #endif
875
876 // If the database doesn't have a place to track progress, assume the worst.
877 // This will happen when new databases are created.
878 if (!DoesTableExist("meta")) {
879 RecordOneEvent(EVENT_MMAP_META_MISSING);
880 return 0;
881 }
882
883 // Key into meta table to get status from a previous run. The value
884 // represents how much data in bytes has successfully been read from the
885 // database. |kMmapFailure| indicates that there was a read error and the
886 // database should not be memory-mapped, while |kMmapSuccess| indicates that
887 // the entire file was read at some point and can be memory-mapped without
888 // constraint.
889 const char* kMmapStatusKey = "mmap_status";
890 static const sqlite3_int64 kMmapFailure = -2;
891 static const sqlite3_int64 kMmapSuccess = -1;
892
893 // Start reading from 0 unless status is found in meta table.
894 sqlite3_int64 mmap_ofs = 0;
895
896 // Retrieve the current status. It is fine for the status to be missing
897 // entirely, but any error prevents memory-mapping.
898 {
899 const char* kMmapStatusSql = "SELECT value FROM meta WHERE key = ?";
900 Statement s(GetUniqueStatement(kMmapStatusSql));
901 s.BindString(0, kMmapStatusKey);
902 if (s.Step()) {
903 mmap_ofs = s.ColumnInt64(0);
904 } else if (!s.Succeeded()) {
905 RecordOneEvent(EVENT_MMAP_META_FAILURE_READ);
906 return 0;
907 }
908 }
909
910 // Database read failed in the past, don't memory map.
911 if (mmap_ofs == kMmapFailure) {
912 RecordOneEvent(EVENT_MMAP_FAILED);
913 return 0;
914 } else if (mmap_ofs != kMmapSuccess) {
915 // Continue reading from previous offset.
916 DCHECK_GE(mmap_ofs, 0);
917
918 // TODO(shess): Could this reading code be shared with Preload()? It would
919 // require locking twice (this code wouldn't be able to access |db_size| so
920 // the helper would have to return amount read).
921
922 // Read more of the database looking for errors. The VFS interface is used
923 // to assure that the reads are valid for SQLite. |g_reads_allowed| is used
924 // to limit checking to 20MB per run of Chromium.
925 sqlite3_file* file = NULL;
926 sqlite3_int64 db_size = 0;
927 int rc = GetSqlite3FileAndSize(db_, &file, &db_size);
928 if (rc != SQLITE_OK) {
929 RecordOneEvent(EVENT_MMAP_VFS_FAILURE);
930 return 0;
931 }
932
933 // Read the data left, or |g_reads_allowed|, whichever is smaller.
934 // |g_reads_allowed| limits the total amount of I/O to spend verifying data
935 // in a single Chromium run.
936 sqlite3_int64 amount = db_size - mmap_ofs;
937 if (amount < 0)
938 amount = 0;
rmcilroy 2015/11/05 15:34:46 If the database got truncated between the previous
Scott Hess - ex-Googler 2015/11/05 18:25:07 Good catch!
939 if (amount > 0) {
940 base::AutoLock lock(g_sqlite_init_lock.Get());
941 static sqlite3_int64 g_reads_allowed = 20 * 1024 * 1024;
942 if (g_reads_allowed < amount)
943 amount = g_reads_allowed;
944 g_reads_allowed -= amount;
945 }
946
947 if (amount <= 0) {
948 DCHECK_EQ(0, amount);
949 RecordOneEvent(EVENT_MMAP_SUCCESS_NO_PROGRESS);
950 } else {
951 static const int kPageSize = 4096;
952 char buf[kPageSize];
953 while (amount > 0 && mmap_ofs >= 0) {
954 rc = file->pMethods->xRead(file, buf, sizeof(buf), mmap_ofs);
955 if (rc == SQLITE_OK) {
956 mmap_ofs += sizeof(buf);
957 amount -= sizeof(buf);
958 } else if (rc == SQLITE_IOERR_SHORT_READ) {
959 mmap_ofs = db_size;
960 break;
961 } else {
962 mmap_ofs = kMmapFailure;
963 break;
964 }
965 }
966
967 // Log these events after update to distinguish meta update failure.
968 Events event;
969 if (mmap_ofs >= db_size) {
970 mmap_ofs = kMmapSuccess;
971 event = EVENT_MMAP_SUCCESS_NEW;
972 } else if (mmap_ofs > 0) {
973 event = EVENT_MMAP_SUCCESS_PARTIAL;
974 } else {
975 DCHECK_EQ(kMmapFailure, mmap_ofs);
976 event = EVENT_MMAP_FAILED_NEW;
977 }
978
979 const char* kMmapUpdateStatusSql = "REPLACE INTO meta VALUES (?, ?)";
980 Statement s(GetUniqueStatement(kMmapUpdateStatusSql));
981 s.BindString(0, kMmapStatusKey);
982 s.BindInt64(1, mmap_ofs);
983 if (!s.Run()) {
984 RecordOneEvent(EVENT_MMAP_META_FAILURE_UPDATE);
985 return 0;
986 }
987
988 RecordOneEvent(event);
989 }
990 }
991
992 if (mmap_ofs == kMmapFailure)
993 return 0;
994 if (mmap_ofs == kMmapSuccess)
995 return 256 * 1024 * 1024;
996 return mmap_ofs;
997 }
998
858 void Connection::TrimMemory(bool aggressively) { 999 void Connection::TrimMemory(bool aggressively) {
859 if (!db_) 1000 if (!db_)
860 return; 1001 return;
861 1002
862 // TODO(shess): investigate using sqlite3_db_release_memory() when possible. 1003 // TODO(shess): investigate using sqlite3_db_release_memory() when possible.
863 int original_cache_size; 1004 int original_cache_size;
864 { 1005 {
865 Statement sql_get_original(GetUniqueStatement("PRAGMA cache_size")); 1006 Statement sql_get_original(GetUniqueStatement("PRAGMA cache_size"));
866 if (!sql_get_original.Step()) { 1007 if (!sql_get_original.Step()) {
867 DLOG(WARNING) << "Could not get cache size " << GetErrorMessage(); 1008 DLOG(WARNING) << "Could not get cache size " << GetErrorMessage();
(...skipping 753 matching lines...) Expand 10 before | Expand all | Expand 10 after
1621 1762
1622 // http://www.sqlite.org/pragma.html#pragma_journal_mode 1763 // http://www.sqlite.org/pragma.html#pragma_journal_mode
1623 // DELETE (default) - delete -journal file to commit. 1764 // DELETE (default) - delete -journal file to commit.
1624 // TRUNCATE - truncate -journal file to commit. 1765 // TRUNCATE - truncate -journal file to commit.
1625 // PERSIST - zero out header of -journal file to commit. 1766 // PERSIST - zero out header of -journal file to commit.
1626 // TRUNCATE should be faster than DELETE because it won't need directory 1767 // TRUNCATE should be faster than DELETE because it won't need directory
1627 // changes for each transaction. PERSIST may break the spirit of using 1768 // changes for each transaction. PERSIST may break the spirit of using
1628 // secure_delete. 1769 // secure_delete.
1629 ignore_result(Execute("PRAGMA journal_mode = TRUNCATE")); 1770 ignore_result(Execute("PRAGMA journal_mode = TRUNCATE"));
1630 1771
1631 // Enable memory-mapped access. This value will be capped by
1632 // SQLITE_MAX_MMAP_SIZE, which could be different between 32-bit and 64-bit
1633 // platforms.
1634 mmap_enabled_ = false;
1635 if (!mmap_disabled_)
1636 ignore_result(Execute("PRAGMA mmap_size = 268435456")); // 256MB.
1637 {
1638 Statement s(GetUniqueStatement("PRAGMA mmap_size"));
1639 if (s.Step() && s.ColumnInt64(0) > 0)
1640 mmap_enabled_ = true;
1641 }
1642
1643 const base::TimeDelta kBusyTimeout = 1772 const base::TimeDelta kBusyTimeout =
1644 base::TimeDelta::FromSeconds(kBusyTimeoutSeconds); 1773 base::TimeDelta::FromSeconds(kBusyTimeoutSeconds);
1645 1774
1646 if (page_size_ != 0) { 1775 if (page_size_ != 0) {
1647 // Enforce SQLite restrictions on |page_size_|. 1776 // Enforce SQLite restrictions on |page_size_|.
1648 DCHECK(!(page_size_ & (page_size_ - 1))) 1777 DCHECK(!(page_size_ & (page_size_ - 1)))
1649 << " page_size_ " << page_size_ << " is not a power of two."; 1778 << " page_size_ " << page_size_ << " is not a power of two.";
1650 const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h 1779 const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h
1651 DCHECK_LE(page_size_, kSqliteMaxPageSize); 1780 DCHECK_LE(page_size_, kSqliteMaxPageSize);
1652 const std::string sql = 1781 const std::string sql =
1653 base::StringPrintf("PRAGMA page_size=%d", page_size_); 1782 base::StringPrintf("PRAGMA page_size=%d", page_size_);
1654 ignore_result(ExecuteWithTimeout(sql.c_str(), kBusyTimeout)); 1783 ignore_result(ExecuteWithTimeout(sql.c_str(), kBusyTimeout));
1655 } 1784 }
1656 1785
1657 if (cache_size_ != 0) { 1786 if (cache_size_ != 0) {
1658 const std::string sql = 1787 const std::string sql =
1659 base::StringPrintf("PRAGMA cache_size=%d", cache_size_); 1788 base::StringPrintf("PRAGMA cache_size=%d", cache_size_);
1660 ignore_result(ExecuteWithTimeout(sql.c_str(), kBusyTimeout)); 1789 ignore_result(ExecuteWithTimeout(sql.c_str(), kBusyTimeout));
1661 } 1790 }
1662 1791
1663 if (!ExecuteWithTimeout("PRAGMA secure_delete=ON", kBusyTimeout)) { 1792 if (!ExecuteWithTimeout("PRAGMA secure_delete=ON", kBusyTimeout)) {
1664 bool was_poisoned = poisoned_; 1793 bool was_poisoned = poisoned_;
1665 Close(); 1794 Close();
1666 if (was_poisoned && retry_flag == RETRY_ON_POISON) 1795 if (was_poisoned && retry_flag == RETRY_ON_POISON)
1667 return OpenInternal(file_name, NO_RETRY); 1796 return OpenInternal(file_name, NO_RETRY);
1668 return false; 1797 return false;
1669 } 1798 }
1670 1799
1800 // Enable memory-mapped access. The explicit-disable case is because SQLite
1801 // can be built to default-enable mmap. MmapHelper() calculates a safe range
1802 // to memory-map based on past regular I/O. This value will be capped by
1803 // SQLITE_MAX_MMAP_SIZE, which could be different between 32-bit and 64-bit
1804 // platforms.
1805 size_t mmap_size = mmap_disabled_ ? 0 : MmapHelper();
1806 std::string mmap_sql =
1807 base::StringPrintf("PRAGMA mmap_size = %" PRIuS, mmap_size);
1808 ignore_result(Execute(mmap_sql.c_str()));
1809
1810 // Determine if memory-mapping has actually been enabled. The Execute() above
1811 // can succeed without changing the amount mapped.
1812 mmap_enabled_ = false;
1813 {
1814 Statement s(GetUniqueStatement("PRAGMA mmap_size"));
1815 if (s.Step() && s.ColumnInt64(0) > 0)
1816 mmap_enabled_ = true;
1817 }
1818
1671 return true; 1819 return true;
1672 } 1820 }
1673 1821
1674 void Connection::DoRollback() { 1822 void Connection::DoRollback() {
1675 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK")); 1823 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK"));
1676 1824
1677 // Collect the rollback time manually, sql::Statement would register it as 1825 // Collect the rollback time manually, sql::Statement would register it as
1678 // query time only. 1826 // query time only.
1679 const base::TimeTicks before = Now(); 1827 const base::TimeTicks before = Now();
1680 rollback.RunWithoutTimers(); 1828 rollback.RunWithoutTimers();
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
1800 ignore_result(Execute(kNoWritableSchema)); 1948 ignore_result(Execute(kNoWritableSchema));
1801 1949
1802 return ret; 1950 return ret;
1803 } 1951 }
1804 1952
1805 base::TimeTicks TimeSource::Now() { 1953 base::TimeTicks TimeSource::Now() {
1806 return base::TimeTicks::Now(); 1954 return base::TimeTicks::Now();
1807 } 1955 }
1808 1956
1809 } // namespace sql 1957 } // namespace sql
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698