Skip to content

sqlite: reject statement-less SQL in SQLTagStore - #65157

Draft
TrevorBurnham wants to merge 1 commit into
nodejs:mainfrom
TrevorBurnham:sqlite/fix-tagstore-null-stmt
Draft

sqlite: reject statement-less SQL in SQLTagStore#65157
TrevorBurnham wants to merge 1 commit into
nodejs:mainfrom
TrevorBurnham:sqlite/fix-tagstore-null-stmt

Conversation

@TrevorBurnham

Copy link
Copy Markdown
Contributor

sqlite3_prepare_v2() returns SQLITE_OK without producing a statement when its input holds no SQL, such as a comment. SQLTagStore::PrepareStatement() only checked the return code, so it cached a StatementSync wrapping a null sqlite3_stmt. Executing it reached sqlite3_clear_bindings(), which only guards against a null statement under SQLITE_ENABLE_API_ARMOR, and segfaulted:

const db = new DatabaseSync(':memory:');
const store = db.createTagStore();
store.run`-- comment`;  // segfault

All four query methods (run, get, all, iterate) were affected. This now throws ERR_INVALID_ARG_VALUE instead, and the rejected statement is not cached.

The StatementSync methods already avoid the crash because their IsFinalized() guard treats a null statement as finalized.

Fixes: #65149

sqlite3_prepare_v2() returns SQLITE_OK without producing a statement
when its input holds no SQL, such as a comment. PrepareStatement() only
checked the return code, so it cached a StatementSync wrapping a null
sqlite3_stmt. Executing it reached sqlite3_clear_bindings(), which only
guards against a null statement under SQLITE_ENABLE_API_ARMOR, and
segfaulted.

Reject such input instead of caching it. The StatementSync methods
already avoid the crash because their IsFinalized() guard treats a null
statement as finalized.

Fixes: nodejs#65149

Signed-off-by: Trevor Burnham <trevorburnham@gmail.com>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/sqlite

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run. sqlite Issues and PRs related to the SQLite subsystem. labels Aug 9, 2026
Comment thread src/node_sqlite.cc
Comment on lines +3651 to +3657
// sqlite3_prepare_v2() reports success without producing a statement when
// the input holds no SQL, such as a comment. Such a statement cannot be
// bound or executed, so reject it instead of caching it.
if (s == nullptr) {
THROW_ERR_INVALID_ARG_VALUE(env, "The SQL query contains no statement.");
return BaseObjectPtr<StatementSync>();
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the right approach, but if we do this for the tag store then we should also perform this check for db.prepare() (ie. throw at the point of preparation rather than at the point of attempting to step the null query).

const stmt = db.prepare('-- :-/') // currently succeeds
stmt.get() // currently fails at this point with ERR_INVALID_STATE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run. sqlite Issues and PRs related to the SQLite subsystem.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sqlite: segfault when a SQLTagStore query contains only a comment

3 participants