Commit 6c41b439 authored by Maks Orlovich's avatar Maks Orlovich Committed by Commit Bot

SQLitePersistentCookieStore: recover from uniqueness violation on V9->V10 migration

We can do better than just dropping the DB, by dropping just the older
versions of the duplicate cookie --- this is how M65 would behave when
loading the store, so it preserves old behavior.

This is probably too late for Chrome, but may help other embedders.

Bug: 835990
Change-Id: I25bf87d46574346a47c05b35152fa3c42f9aa7a9
Reviewed-on: https://chromium-review.googlesource.com/1097204Reviewed-by: default avatarVictor Costan <pwnall@chromium.org>
Commit-Queue: Maks Orlovich <morlovich@chromium.org>
Cr-Commit-Position: refs/heads/master@{#567018}
parent 2dfa922e
...@@ -1178,26 +1178,18 @@ bool SQLitePersistentCookieStore::Backend::EnsureDatabaseVersion() { ...@@ -1178,26 +1178,18 @@ bool SQLitePersistentCookieStore::Backend::EnsureDatabaseVersion() {
return false; return false;
} }
// If any cookies violate the new uniqueness constraints (no two // If any cookies violate the new uniqueness constraints (no two
// cookies with the same (name, domain, path)) erase the cookie store. // cookies with the same (name, domain, path)), pick the newer version,
// That "shouldn't happen", which means probably not too many users' // since that's what CookieMonster would do anyway.
// cookie stores will have it.
// The choice to drop rather than pick one of the cookies randomly is
// because it is expected that servers will be able to deal with a known
// state (no cookies == first visit), and there may be cookie values they
// may not be able to deal with.
if (!db_->Execute( if (!db_->Execute(
"INSERT OR FAIL INTO cookies " "INSERT OR REPLACE INTO cookies "
"(creation_utc, host_key, name, value, path, expires_utc, " "(creation_utc, host_key, name, value, path, expires_utc, "
"is_secure, is_httponly, last_access_utc, has_expires, " "is_secure, is_httponly, last_access_utc, has_expires, "
"is_persistent, priority, encrypted_value, firstpartyonly) " "is_persistent, priority, encrypted_value, firstpartyonly) "
"SELECT creation_utc, host_key, name, value, path, expires_utc, " "SELECT creation_utc, host_key, name, value, path, expires_utc, "
" secure, httponly, last_access_utc, has_expires, " " secure, httponly, last_access_utc, has_expires, "
" persistent, priority, encrypted_value, firstpartyonly " " persistent, priority, encrypted_value, firstpartyonly "
"FROM cookies_old")) { "FROM cookies_old ORDER BY creation_utc ASC")) {
// The old database had duplicate cookies in a way that violates return false;
// the spec. Treat that as DB corruption and start with a clean slate.
if (!db_->Execute("DELETE FROM COOKIES;"))
return false;
} }
if (!db_->Execute("DROP TABLE cookies_old")) if (!db_->Execute("DROP TABLE cookies_old"))
return false; return false;
......
...@@ -963,6 +963,9 @@ bool CreateV9Schema(sql::Connection* db) { ...@@ -963,6 +963,9 @@ bool CreateV9Schema(sql::Connection* db) {
return true; return true;
} }
bool AddV9CookiesToDBImpl(sql::Connection* db,
const std::vector<CanonicalCookie>& cookies);
// Add a selection of cookies to the DB. // Add a selection of cookies to the DB.
bool AddV9CookiesToDB(sql::Connection* db) { bool AddV9CookiesToDB(sql::Connection* db) {
static base::Time cookie_time(base::Time::Now()); static base::Time cookie_time(base::Time::Now());
...@@ -992,7 +995,11 @@ bool AddV9CookiesToDB(sql::Connection* db) { ...@@ -992,7 +995,11 @@ bool AddV9CookiesToDB(sql::Connection* db) {
"C", "B", "example.com", "/path", cookie_time, cookie_time, cookie_time, "C", "B", "example.com", "/path", cookie_time, cookie_time, cookie_time,
false, false, CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT)); false, false, CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT));
cookie_time += base::TimeDelta::FromMicroseconds(1); cookie_time += base::TimeDelta::FromMicroseconds(1);
return AddV9CookiesToDBImpl(db, cookies);
}
bool AddV9CookiesToDBImpl(sql::Connection* db,
const std::vector<CanonicalCookie>& cookies) {
sql::Statement add_smt(db->GetCachedStatement( sql::Statement add_smt(db->GetCachedStatement(
SQL_FROM_HERE, SQL_FROM_HERE,
"INSERT INTO cookies (creation_utc, host_key, name, value, " "INSERT INTO cookies (creation_utc, host_key, name, value, "
...@@ -1045,31 +1052,37 @@ void ConfirmV9CookiesFromDB( ...@@ -1045,31 +1052,37 @@ void ConfirmV9CookiesFromDB(
std::sort(read_in_cookies.begin(), read_in_cookies.end(), &CompareCookies); std::sort(read_in_cookies.begin(), read_in_cookies.end(), &CompareCookies);
int i = 0; int i = 0;
EXPECT_EQ("A", read_in_cookies[i]->Name()); EXPECT_EQ("A", read_in_cookies[i]->Name());
EXPECT_EQ("B", read_in_cookies[i]->Value());
EXPECT_EQ("example.com", read_in_cookies[i]->Domain()); EXPECT_EQ("example.com", read_in_cookies[i]->Domain());
EXPECT_EQ("/", read_in_cookies[i]->Path()); EXPECT_EQ("/", read_in_cookies[i]->Path());
i++; i++;
EXPECT_EQ("A", read_in_cookies[i]->Name()); EXPECT_EQ("A", read_in_cookies[i]->Name());
EXPECT_EQ("B", read_in_cookies[i]->Value());
EXPECT_EQ("example.com", read_in_cookies[i]->Domain()); EXPECT_EQ("example.com", read_in_cookies[i]->Domain());
EXPECT_EQ("/path", read_in_cookies[i]->Path()); EXPECT_EQ("/path", read_in_cookies[i]->Path());
i++; i++;
EXPECT_EQ("A", read_in_cookies[i]->Name()); EXPECT_EQ("A", read_in_cookies[i]->Name());
EXPECT_EQ("B", read_in_cookies[i]->Value());
EXPECT_EQ("example2.com", read_in_cookies[i]->Domain()); EXPECT_EQ("example2.com", read_in_cookies[i]->Domain());
EXPECT_EQ("/", read_in_cookies[i]->Path()); EXPECT_EQ("/", read_in_cookies[i]->Path());
i++; i++;
EXPECT_EQ("C", read_in_cookies[i]->Name()); EXPECT_EQ("C", read_in_cookies[i]->Name());
EXPECT_EQ("B", read_in_cookies[i]->Value());
EXPECT_EQ("example.com", read_in_cookies[i]->Domain()); EXPECT_EQ("example.com", read_in_cookies[i]->Domain());
EXPECT_EQ("/", read_in_cookies[i]->Path()); EXPECT_EQ("/", read_in_cookies[i]->Path());
i++; i++;
EXPECT_EQ("C", read_in_cookies[i]->Name()); EXPECT_EQ("C", read_in_cookies[i]->Name());
EXPECT_EQ("B", read_in_cookies[i]->Value());
EXPECT_EQ("example.com", read_in_cookies[i]->Domain()); EXPECT_EQ("example.com", read_in_cookies[i]->Domain());
EXPECT_EQ("/path", read_in_cookies[i]->Path()); EXPECT_EQ("/path", read_in_cookies[i]->Path());
i++; i++;
EXPECT_EQ("C", read_in_cookies[i]->Name()); EXPECT_EQ("C", read_in_cookies[i]->Name());
EXPECT_EQ("B", read_in_cookies[i]->Value());
EXPECT_EQ("example2.com", read_in_cookies[i]->Domain()); EXPECT_EQ("example2.com", read_in_cookies[i]->Domain());
EXPECT_EQ("/", read_in_cookies[i]->Path()); EXPECT_EQ("/", read_in_cookies[i]->Path());
} }
...@@ -1100,16 +1113,30 @@ TEST_F(SQLitePersistentCookieStoreTest, UpgradeToSchemaVersion10Corrupted) { ...@@ -1100,16 +1113,30 @@ TEST_F(SQLitePersistentCookieStoreTest, UpgradeToSchemaVersion10Corrupted) {
ASSERT_TRUE(CreateV9Schema(&connection)); ASSERT_TRUE(CreateV9Schema(&connection));
base::Time old_time = base::Time::Now() - base::TimeDelta::FromMinutes(90);
base::Time old_time2 = base::Time::Now() - base::TimeDelta::FromMinutes(91);
CanonicalCookie old_cookie1(
"A", "old_value", "example.com", "/", old_time, old_time, old_time, false,
false, CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT);
AddV9CookiesToDBImpl(&connection, {old_cookie1});
// Add the same set of cookies twice to create duplicates. // Add the same set of cookies twice to create duplicates.
ASSERT_TRUE(AddV9CookiesToDB(&connection)); ASSERT_TRUE(AddV9CookiesToDB(&connection));
ASSERT_TRUE(AddV9CookiesToDB(&connection)); ASSERT_TRUE(AddV9CookiesToDB(&connection));
// Add some others as well.
CanonicalCookie old_cookie2(
"A", "old_value", "example.com", "/path", old_time2, old_time2, old_time2,
false, false, CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT);
AddV9CookiesToDBImpl(&connection, {old_cookie2});
connection.Close(); connection.Close();
std::vector<std::unique_ptr<CanonicalCookie>> read_in_cookies; std::vector<std::unique_ptr<CanonicalCookie>> read_in_cookies;
CreateAndLoad(false, false, &read_in_cookies); CreateAndLoad(false, false, &read_in_cookies);
// Finding failures of the uniqueness constraint should result in a nuked DB. // Finding failures of the uniqueness constraint should resolve them by
EXPECT_EQ(0u, read_in_cookies.size()); // timestamp.
ConfirmV9CookiesFromDB(std::move(read_in_cookies));
} }
// Confirm the store can handle having cookies with identical creation // Confirm the store can handle having cookies with identical creation
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment