Revert "SQLite: Set printf() precision limits in production."
This reverts commit 389b0c0306
.
Reason for revert: https://crbug.com/1212989 -- Turns out printf() is used internally in SQLite's schema parsing, and precision limits break it.
Original change's description:
> SQLite: Set printf() precision limits in production.
>
> This CL sets an explicit precision limit on printf() according to the
> recommendations at
> https://www.sqlite.org/compile.html#printf_precision_limit.
>
> printf() is Web-exposed in WebSQL since Chrome 91, so we have a short
> window of opportunity to set limits, before Web properties start
> depending on the function. These limits reduce the chance that a bug in
> a site will accidentally OOM a renderer.
>
> Bug: 1211778
> Change-Id: Iff7221705761d5d5a1523051f4d6ca932da34492
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2911717
> Commit-Queue: Victor Costan <pwnall@chromium.org>
> Commit-Queue: Darwin Huang <huangdarwin@chromium.org>
> Auto-Submit: Victor Costan <pwnall@chromium.org>
> Reviewed-by: Darwin Huang <huangdarwin@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#886041}
Bug: 1211778
Change-Id: I61292fe3467e10f28d57b01c77efcfb3516ff1f4
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2917235
Auto-Submit: Victor Costan <pwnall@chromium.org>
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/heads/master@{#886323}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
510e529215
commit
1141e10e43
@ -297,47 +297,6 @@ TEST_F(SQLiteFeaturesTest, CachedRegexp) {
|
||||
EXPECT_EQ(7, s.ColumnInt(0));
|
||||
}
|
||||
|
||||
TEST_F(SQLiteFeaturesTest, PrintfPrecision_WidthAboveLimit) {
|
||||
sql::Statement statement(
|
||||
db_.GetUniqueStatement("SELECT printf('%1001d', 1)"));
|
||||
ASSERT_TRUE(statement.Step());
|
||||
EXPECT_EQ(std::string(999, ' ') + "1", statement.ColumnString(0));
|
||||
}
|
||||
|
||||
TEST_F(SQLiteFeaturesTest, PrintfPrecision_WidthAtLimit) {
|
||||
sql::Statement statement(
|
||||
db_.GetUniqueStatement("SELECT printf('%1000d', 1)"));
|
||||
ASSERT_TRUE(statement.Step());
|
||||
EXPECT_EQ(std::string(999, ' ') + "1", statement.ColumnString(0));
|
||||
}
|
||||
|
||||
TEST_F(SQLiteFeaturesTest, PrintfPrecision_WidthBelowLimit) {
|
||||
sql::Statement statement(db_.GetUniqueStatement("SELECT printf('%999d', 1)"));
|
||||
ASSERT_TRUE(statement.Step());
|
||||
EXPECT_EQ(std::string(998, ' ') + "1", statement.ColumnString(0));
|
||||
}
|
||||
|
||||
TEST_F(SQLiteFeaturesTest, PrintfPrecision_PrecisionAboveLimit) {
|
||||
sql::Statement statement(
|
||||
db_.GetUniqueStatement("SELECT printf('%.1001d', 1)"));
|
||||
ASSERT_TRUE(statement.Step());
|
||||
EXPECT_EQ(std::string(999, '0') + "1", statement.ColumnString(0));
|
||||
}
|
||||
|
||||
TEST_F(SQLiteFeaturesTest, PrintfPrecision_PrecisionAtLimit) {
|
||||
sql::Statement statement(
|
||||
db_.GetUniqueStatement("SELECT printf('%.1000d', 1)"));
|
||||
ASSERT_TRUE(statement.Step());
|
||||
EXPECT_EQ(std::string(999, '0') + "1", statement.ColumnString(0));
|
||||
}
|
||||
|
||||
TEST_F(SQLiteFeaturesTest, PrintfPrecision_PrecisionBelowLimit) {
|
||||
sql::Statement statement(
|
||||
db_.GetUniqueStatement("SELECT printf('%.999d', 1)"));
|
||||
ASSERT_TRUE(statement.Step());
|
||||
EXPECT_EQ(std::string(998, '0') + "1", statement.ColumnString(0));
|
||||
}
|
||||
|
||||
#if defined(OS_MAC)
|
||||
// If a database file is marked to be excluded from Time Machine, verify that
|
||||
// journal files are also excluded.
|
||||
|
@ -1,61 +0,0 @@
|
||||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>WebSQL: printf() does not OOM with large strings</title>
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(testCase => {
|
||||
const database = openDatabase(
|
||||
'PrintfTest', '1.0', 'Database for printf() test',
|
||||
1024 * 1024);
|
||||
assert_not_equals(database, null, 'openDatabase should not fail');
|
||||
|
||||
database.transaction(testCase.step_func(transaction => {
|
||||
transaction.executeSql(
|
||||
'DROP TABLE IF EXISTS main;', [], () => {},
|
||||
testCase.unreached_func('DROP TABLE should not fail'));
|
||||
transaction.executeSql(
|
||||
'CREATE TABLE main(id INTEGER PRIMARY KEY);', [], () => {},
|
||||
testCase.unreached_func('CREATE TABLE should not fail'));
|
||||
transaction.executeSql(
|
||||
'INSERT INTO main VALUES(1);', [], () => {},
|
||||
testCase.unreached_func('INSERT should not fail'));
|
||||
|
||||
// When SQLITE_PRINTF_PRECISION_LIMIT is not overridden, the printf()
|
||||
// statement below produces a string that takes up slightly less than 1GiB
|
||||
// of RAM.
|
||||
//
|
||||
// SQLite's printf buffer is limited by SQLITE_PRINTF_PRECISION_LIMIT
|
||||
// and SQLITE_LIMIT_LENGTH [1]. The latter defaults to
|
||||
// SQLITE_MAX_LENGTH [2], and Chrome does not override this default via
|
||||
// sqlite3_limit() [3]. Chrome uses SQLite's default SQLITE_MAX_LENGTH,
|
||||
// which is 1,000,000,000.
|
||||
//
|
||||
// The statement uses the %X (hexadecimal) specifier instead of the more
|
||||
// familiar %d (decimal) in order to reduce the CPU time taken by a failing
|
||||
// test. More specifically, in case SQLite ends up doing division for each
|
||||
// output character (1 billion times, if SQLITE_PRINTF_PRECISION_LIMIT isn't
|
||||
// set), the savings of bit-shifting (used by hexadecimal) over full
|
||||
// division (required by decimal) can add up.
|
||||
//
|
||||
// [1] https://sqlite.org/compile.html#printf_precision_limit and
|
||||
// [2] https://sqlite.org/limits.html#max_length.
|
||||
// [3] https://sqlite.org/c3ref/c_limit_attached.html#sqlitelimitlength
|
||||
const kPrintf1GB = "printf('%1000000000X', id)";
|
||||
|
||||
// Chrome renderers are currently limited to using 4GiB of RAM.
|
||||
// of these strings OOM the renderer. 5 printf() statements will produce
|
||||
// strings whose combined lengths exceeds 4GiB of RAM.
|
||||
//
|
||||
// If this test completes, it means it hasn't OOMed, and
|
||||
// SQLITE_PRINTF_PRECISION_LIMIT is set to less than 1,000,000,000.
|
||||
transaction.executeSql(
|
||||
`SELECT ${kPrintf1GB}, ${kPrintf1GB}, ${kPrintf1GB}, ${kPrintf1GB}, ` +
|
||||
`${kPrintf1GB} FROM main;`,
|
||||
[], testCase.step_func_done(() => {}),
|
||||
testCase.unreached_func('printf() should not fail'));
|
||||
}));
|
||||
}, `printf() should not fail or crash with %1000000000X`);
|
||||
</script>
|
41
third_party/sqlite/BUILD.gn
vendored
41
third_party/sqlite/BUILD.gn
vendored
@ -56,26 +56,12 @@ config("common_sqlite3_compile_options") {
|
||||
defines += [ "SQLITE_ENABLE_LOCKING_STYLE=0" ]
|
||||
}
|
||||
|
||||
# The width and precision fields in SQLite's printf() function may easily lead
|
||||
# to accidental OOMs.
|
||||
#
|
||||
# In production builds, we limit the fields to the value recommended at
|
||||
# https://www.sqlite.org/compile.html#printf_precision_limit. Fuzzing builds
|
||||
# use a larger limit, because our fuzzers use printf()'s ability to create
|
||||
# large buffers to test edge cases in SQLite's buffer handling code.
|
||||
#
|
||||
# See also https://sqlite.org/printf.html
|
||||
if (use_fuzzing_engine) {
|
||||
defines += [ "SQLITE_PRINTF_PRECISION_LIMIT=128000000" ] # 128 MB
|
||||
} else {
|
||||
defines += [ "SQLITE_PRINTF_PRECISION_LIMIT=1000" ]
|
||||
}
|
||||
|
||||
if (use_fuzzing_engine) {
|
||||
# Limit max length of data blobs and queries to 128 MB for fuzzing builds.
|
||||
if (using_sanitizer) {
|
||||
defines += [
|
||||
# Limit max length of data blobs and queries to 128 MB for fuzzing builds.
|
||||
"SQLITE_MAX_LENGTH=128000000",
|
||||
"SQLITE_MAX_SQL_LENGTH=128000000",
|
||||
"SQLITE_PRINTF_PRECISION_LIMIT=1280000",
|
||||
]
|
||||
|
||||
# During fuzz testing, valid SQL queries generated by fuzzing engine may
|
||||
@ -83,17 +69,20 @@ config("common_sqlite3_compile_options") {
|
||||
# out-of-memory error. However, such errors are not valid bugs.
|
||||
# To avoid hitting those irrelevant OOMs, we limit max number of memory
|
||||
# pages, so fuzzer will not crash when reaching the limit.
|
||||
defines += [
|
||||
"SQLITE_MAX_PAGE_COUNT=16384",
|
||||
# Apply this for fuzzing builds only, not for all builds with sanitizers.
|
||||
if (use_fuzzing_engine) {
|
||||
defines += [
|
||||
"SQLITE_MAX_PAGE_COUNT=16384",
|
||||
|
||||
# Used to deserialize a database from a libfuzzer-provided data blob.
|
||||
# This is to fuzz SQLite's resilience to database corruption.
|
||||
"SQLITE_ENABLE_DESERIALIZE",
|
||||
]
|
||||
# Used to deserialize a database from a libfuzzer-provided data blob.
|
||||
# This is to fuzz SQLite's resilience to database corruption.
|
||||
"SQLITE_ENABLE_DESERIALIZE",
|
||||
]
|
||||
|
||||
# The progress callback is used in fuzzing to cancel long-running queries
|
||||
# so we don't spend too much time on them.
|
||||
defines -= [ "SQLITE_OMIT_PROGRESS_CALLBACK" ]
|
||||
# The progress callback is used in fuzzing to cancel long-running queries
|
||||
# so we don't spend too much time on them.
|
||||
defines -= [ "SQLITE_OMIT_PROGRESS_CALLBACK" ]
|
||||
}
|
||||
}
|
||||
|
||||
if (is_debug || dcheck_always_on) {
|
||||
|
Reference in New Issue
Block a user