0

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:
Victor Costan
2021-05-25 16:42:10 +00:00
committed by Chromium LUCI CQ
parent 510e529215
commit 1141e10e43
3 changed files with 15 additions and 128 deletions
sql
third_party
blink
web_tests
storage
sqlite

@ -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>

@ -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) {