sqlite: fix crashes on invalid backup and URL args by lazerg · Pull Request #65831 · nodejs/node · GitHub
Skip to content

sqlite: fix crashes on invalid backup and URL args - #65831

Open
lazerg wants to merge 2 commits into
nodejs:mainfrom
lazerg:fix/issue-65830-sqlite-arg-validation
Open

sqlite: fix crashes on invalid backup and URL args#65831
lazerg wants to merge 2 commits into
nodejs:mainfrom
lazerg:fix/issue-65830-sqlite-arg-validation

Conversation

@lazerg

@lazerg lazerg commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

backup() checked only that its first argument was an object before unwrapping it as a DatabaseSync, so backup({}, path) crashed with a segfault. ValidateDatabasePath() had a similar problem: it asserted that a duck-typed URL's href parses, which aborts the process rather than throwing, so new DatabaseSync({ href: 'zzz' }) took the whole process down too.

Both now throw a TypeError instead.

Fixes: #65830

Signed-off-by: Lazizbek Ergashev <lazerg2@gmail.com>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@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 Sep 6, 2026
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Caution

AgentScan found account activity patterns that may be consistent with automation. This is a heuristic, not proof that this pull request was opened by an agent or violates policy. AI-assisted contributions are permitted, but automated tooling must not open pull requests without advance approval, and contributors must personally understand, test, verify, and take responsibility for every submitted change. See the AgentScan analysis, AI use policy, and automation policy for additional context.

@lazerg

lazerg commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

I used agent to resolve the problem. I confirm I revied code myself.

@TrevorBurnham

Copy link
Copy Markdown
Contributor

FYI I posted a draft PR around the same time this one went up: #65832 A few things from it may be worth folding in here:

  1. Consider storing the template behind a DatabaseSync::GetConstructorTemplate(env) accessor. That's consistent with established patterns and would avoid re-assigning the slot if Initialize() ever ran for more than one realm.
  2. The foreign-BaseObject cases are worth pinning too. backup({}) covers the plain-object path, but the other variants in the issue fail differently: backup([]) hits FATAL ERROR: GetAlignedPointerFromInternalField() Internal field out of bounds rather than a segfault, and passing a different BaseObject subclass finds a valid pointer in the internal field and reinterprets it at the wrong offsets. { __proto__: DatabaseSync.prototype } would be cheap to add as well.
  3. ValidateDatabasePath() has two callers. backup(db, { href: 'zzz' }) reaches the same CHECK.
  4. Minor: elsewhere in src/ the wording is "must be an instance of X" rather than "must be a X instance".

Store the DatabaseSync constructor template behind
DatabaseSync::GetConstructorTemplate(), matching StatementSync,
StatementSyncIterator, Session and SQLTagStore, which all already have
one. DatabaseSync was the only class in the file setting its template up
inline in Initialize(), which also meant the Environment slot would be
reassigned if Initialize() ever ran for more than one realm, leaving
instances from the earlier realm failing HasInstance(). This moves
SetSideEffectFreeGetter() to the top of the file so the accessor can use
it; its body is unchanged.

Drop the now-redundant IsObject() check in Backup() so a bad sourceDb
reports one message instead of two, and use the wording
lib/internal/errors.js generates for ERR_INVALID_ARG_TYPE.

Stop discarding an exception thrown by an href getter in
ValidateDatabasePath() and replacing it with ERR_INVALID_ARG_TYPE.

Cover the remaining unwrap cases in tests: an array, a padded plain
object, an object with DatabaseSync.prototype, a StatementSync and a
Session, plus the second ValidateDatabasePath() caller reached through
backup(db, { href: 'zzz' }).

Co-authored-by: Trevor Burnham <trevorburnham@gmail.com>
Signed-off-by: Lazizbek Ergashev <lazerg2@gmail.com>
@lazerg

lazerg commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, this was useful. I went through all four against the source and pushed bf0648c.

  1. Done. Four other classes in the file already have a GetConstructorTemplate(), and DatabaseSync was the only one building its template inline in Initialize(), so the accessor is the more consistent shape. The re-assignment point holds: a second Initialize() would build a fresh template and overwrite the slot, and instances from the first realm would then fail HasInstance(). I took your version of the accessor, including moving SetSideEffectFreeGetter() up, and credited you on the commit.

  2. The guard already covered these, since HasInstance() is a template check and does not care what shape of object it gets. Confirmed on a build: [], { p0: 1, p1: 2, p2: 3, p3: 4 }, { __proto__: DatabaseSync.prototype }, a StatementSync and a Session all throw ERR_INVALID_ARG_TYPE. None of them were tested though, so all six are now in the backup test.

  3. Same story. The CHECK is inside ValidateDatabasePath(), so replacing it fixed both callers at once. backup(db, { href: 'zzz' }) now has a test of its own.

  4. Changed. lib/internal/errors.js builds "an instance of X" for ERR_INVALID_ARG_TYPE, which settles it. I also dropped the IsObject() check ahead of the guard, so backup() with no argument gives that message too instead of "must be an object.".

One more from your diff that was not in your list: ValidateDatabasePath() discarded an exception thrown by an href getter and put ERR_INVALID_ARG_TYPE over it. I took that fix as well.

The one place I kept my version is the unparseable href. You fall through to ERR_INVALID_ARG_TYPE; I throw ERR_INVALID_URL, following node_file.cc. Both read fine to me and I have no strong preference, so I am happy to switch if a reviewer prefers yours.

On the two PRs: yours was the more complete one, and most of what is now here came from it. If you would rather land yours, say so and I will close this in its favour. Otherwise I believe this one now covers the same ground.

@codecov

codecov Bot commented Sep 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.18%. Comparing base (6f41e41) to head (bf0648c).

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #65831      +/-   ##
==========================================
+ Coverage   90.17%   90.18%   +0.01%     
==========================================
  Files         770      770              
  Lines      264483   264492       +9     
  Branches    50262    50266       +4     
==========================================
+ Hits       238491   238544      +53     
+ Misses      16981    16938      -43     
+ Partials     9011     9010       -1     
Files with missing lines Coverage Δ
src/node_sqlite.cc 82.32% <100.00%> (+0.08%) ⬆️
src/node_sqlite.h 86.40% <ø> (ø)

... and 28 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Labels

agentscan:community-flagged 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.

node:sqlite: backup() unwraps any object as a DatabaseSync, and a duck-typed URL aborts the process

3 participants