-
Notifications
You must be signed in to change notification settings - Fork 30.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sqlite: fix use-after-free in StatementSync due to premature GC #56840
base: main
Are you sure you want to change the base?
Conversation
9ca3cc0
to
4373e55
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #56840 +/- ##
=======================================
Coverage 89.21% 89.21%
=======================================
Files 665 665
Lines 192514 192513 -1
Branches 37040 37041 +1
=======================================
+ Hits 171744 171753 +9
+ Misses 13613 13606 -7
+ Partials 7157 7154 -3
|
Hm, it looks like there may be related failures on Windows while trying to clean up:
This happened a number of times on Windows. I'll restart the CI to see if that was a one off issue, but I have a feeling it's legit. |
The latest run had the same file files blocking cleanup. They appear to correspond to these five tests. I'm not sure off the top of my head why those ones would be blocking. I do see that the two |
4373e55
to
7185964
Compare
I updated the two |
@littledivy can you apply these changes. These should resolve the test failures now due to a conflicting change that landed. diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc
index 39e1be871d..1639c196ce 100644
--- a/src/node_sqlite.cc
+++ b/src/node_sqlite.cc
@@ -977,7 +977,7 @@ inline bool StatementSync::IsFinalized() {
bool StatementSync::BindParams(const FunctionCallbackInfo<Value>& args) {
int r = sqlite3_clear_bindings(statement_);
- CHECK_ERROR_OR_THROW(env()->isolate(), db_, r, SQLITE_OK, false);
+ CHECK_ERROR_OR_THROW(env()->isolate(), db_.get(), r, SQLITE_OK, false);
int anon_idx = 1;
int anon_start = 0;
@@ -1107,7 +1107,7 @@ bool StatementSync::BindValue(const Local<Value>& value, const int index) {
return false;
}
- CHECK_ERROR_OR_THROW(env()->isolate(), db_, r, SQLITE_OK, false);
+ CHECK_ERROR_OR_THROW(env()->isolate(), db_.get(), r, SQLITE_OK, false);
return true;
}
@@ -1173,7 +1173,7 @@ void StatementSync::All(const FunctionCallbackInfo<Value>& args) {
env, stmt->IsFinalized(), "statement has been finalized");
Isolate* isolate = env->isolate();
int r = sqlite3_reset(stmt->statement_);
- CHECK_ERROR_OR_THROW(isolate, stmt->db_, r, SQLITE_OK, void());
+ CHECK_ERROR_OR_THROW(isolate, stmt->db_.get(), r, SQLITE_OK, void());
if (!stmt->BindParams(args)) {
return;
@@ -1202,7 +1202,7 @@ void StatementSync::All(const FunctionCallbackInfo<Value>& args) {
rows.emplace_back(row);
}
- CHECK_ERROR_OR_THROW(isolate, stmt->db_, r, SQLITE_DONE, void());
+ CHECK_ERROR_OR_THROW(isolate, stmt->db_.get(), r, SQLITE_DONE, void());
args.GetReturnValue().Set(Array::New(isolate, rows.data(), rows.size()));
}
@@ -1270,7 +1270,8 @@ void StatementSync::IterateNextCallback(
int r = sqlite3_step(stmt->statement_);
if (r != SQLITE_ROW) {
- CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_, r, SQLITE_DONE, void());
+ CHECK_ERROR_OR_THROW(
+ env->isolate(), stmt->db_.get(), r, SQLITE_DONE, void());
// cleanup when no more rows to fetch
sqlite3_reset(stmt->statement_);
@@ -1322,7 +1323,7 @@ void StatementSync::Iterate(const FunctionCallbackInfo<Value>& args) {
auto isolate = env->isolate();
auto context = env->context();
int r = sqlite3_reset(stmt->statement_);
- CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_, r, SQLITE_OK, void());
+ CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_.get(), r, SQLITE_OK, void());
if (!stmt->BindParams(args)) {
return;
@@ -1386,7 +1387,7 @@ void StatementSync::Get(const FunctionCallbackInfo<Value>& args) {
env, stmt->IsFinalized(), "statement has been finalized");
Isolate* isolate = env->isolate();
int r = sqlite3_reset(stmt->statement_);
- CHECK_ERROR_OR_THROW(isolate, stmt->db_, r, SQLITE_OK, void());
+ CHECK_ERROR_OR_THROW(isolate, stmt->db_.get(), r, SQLITE_OK, void());
if (!stmt->BindParams(args)) {
return;
@@ -1396,7 +1397,7 @@ void StatementSync::Get(const FunctionCallbackInfo<Value>& args) {
r = sqlite3_step(stmt->statement_);
if (r == SQLITE_DONE) return;
if (r != SQLITE_ROW) {
- THROW_ERR_SQLITE_ERROR(isolate, stmt->db_);
+ THROW_ERR_SQLITE_ERROR(isolate, stmt->db_.get());
return;
}
@@ -1432,7 +1433,7 @@ void StatementSync::Run(const FunctionCallbackInfo<Value>& args) {
THROW_AND_RETURN_ON_BAD_STATE(
env, stmt->IsFinalized(), "statement has been finalized");
int r = sqlite3_reset(stmt->statement_);
- CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_, r, SQLITE_OK, void());
+ CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_.get(), r, SQLITE_OK, void());
if (!stmt->BindParams(args)) {
return;
@@ -1441,7 +1442,7 @@ void StatementSync::Run(const FunctionCallbackInfo<Value>& args) {
auto reset = OnScopeLeave([&]() { sqlite3_reset(stmt->statement_); });
r = sqlite3_step(stmt->statement_);
if (r != SQLITE_ROW && r != SQLITE_DONE) {
- THROW_ERR_SQLITE_ERROR(env->isolate(), stmt->db_);
+ THROW_ERR_SQLITE_ERROR(env->isolate(), stmt->db_.get());
return;
}
@@ -1674,7 +1675,7 @@ void Session::Changeset(const FunctionCallbackInfo<Value>& args) {
void* pChangeset;
int r = sqliteChangesetFunc(session->session_, &nChangeset, &pChangeset);
CHECK_ERROR_OR_THROW(
- env->isolate(), session->database_, r, SQLITE_OK, void());
+ env->isolate(), session->database_.get(), r, SQLITE_OK, void());
auto freeChangeset = OnScopeLeave([&] { sqlite3_free(pChangeset); });
|
This patch updates
StatementSync
to store a strong reference to the database base object.DatabaseSync
may be garbage collected and freed whileStatementSync
is using it (due toMakeWeak()
). The following code crashes with a segmentation fault: