Skip to content
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 segfault in expandedSQL #54687

Merged

Conversation

tniessen
Copy link
Member

@tniessen tniessen commented Sep 1, 2024

The call to sqlite3_expanded_sql() may return NULL depending on various factors. Handle this case instead of running into a segmentation fault.

The call to sqlite3_expanded_sql() may return NULL depending on various
factors. Handle this case instead of running into a segmentation fault.
@tniessen tniessen added c++ Issues and PRs that require attention from people who are familiar with C++. sqlite Issues and PRs related to the SQLite subsystem. labels Sep 1, 2024
@nodejs-github-bot nodejs-github-bot added the needs-ci PRs that need a full CI run. label Sep 1, 2024
Copy link
Contributor

@cjihrig cjihrig left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks. I've had this on my TODO list since coverity reported it a while back.

@targos
Copy link
Member

targos commented Sep 1, 2024

Isn't there a way to test it?

@tniessen
Copy link
Member Author

tniessen commented Sep 1, 2024

@targos It's easiest with different compile-time options, which is what I did locally. I'm not sure if the current JavaScript API allows configuring SQLite in a way that doesn't require a lot of memory for triggering the segmentation fault. I can try that later.

Copy link

codecov bot commented Sep 1, 2024

Codecov Report

Attention: Patch coverage is 57.14286% with 6 lines in your changes missing coverage. Please review.

Project coverage is 87.60%. Comparing base (4c844a2) to head (d4685f0).
Report is 80 commits behind head on main.

Files with missing lines Patch % Lines
src/node_sqlite.cc 57.14% 5 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #54687      +/-   ##
==========================================
+ Coverage   87.33%   87.60%   +0.27%     
==========================================
  Files         650      650              
  Lines      182832   182843      +11     
  Branches    35067    35384     +317     
==========================================
+ Hits       159670   160184     +514     
+ Misses      16421    15937     -484     
+ Partials     6741     6722      -19     
Files with missing lines Coverage Δ
src/node_sqlite.cc 83.05% <57.14%> (-0.95%) ⬇️

... and 67 files with indirect coverage changes

@tniessen
Copy link
Member Author

tniessen commented Sep 2, 2024

@targos Here is a demonstration using the default options and the official Node.js 22.7.0 binary:

const { DatabaseSync } = require('node:sqlite');

const db = new DatabaseSync('tmp.sqlite');
db.exec('CREATE TABLE a (t TEXT NOT NULL) STRICT;');

const targetStringSize = 1024 * 1024 * 1024;

const nRows = 10_000;
const valueLength = targetStringSize / nRows;

const stmt = db.prepare(`INSERT INTO a (t) VALUES ${[...Array(nRows).keys()].map(() => '(?)').join(',')};`);
const value = 'A'.repeat(valueLength);
const allValues = [...Array(nRows).keys()].map(() => value);
stmt.run(...allValues);
console.log(stmt.expandedSQL());

The segmentation fault occurs after a few seconds:

$ node -v
v22.7.0
$ time node --experimental-sqlite trigger-it.js 
Segmentation fault (core dumped)

real    0m3.872s
user    0m1.444s
sys     0m1.590s

With this PR, instead, this happens:

$ ./node --experimental-sqlite trigger-it.js 
/home/tniessen/dev/node/trigger-it.js:15
console.log(stmt.expandedSQL());
                 ^

Error: Expanded SQL text would exceed configured limits
    at Object.<anonymous> (/home/tniessen/dev/node/trigger-it.js:15:18)
    at Module._compile (node:internal/modules/cjs/loader:1546:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1691:10)
    at Module.load (node:internal/modules/cjs/loader:1317:32)
    at Module._load (node:internal/modules/cjs/loader:1127:12)
    at TracingChannel.traceSync (node:diagnostics_channel:315:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:217:24)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:166:5)
    at node:internal/main/run_main_module:30:49 {
  code: 'ERR_SQLITE_ERROR'
}

Node.js v23.0.0-pre

However, this test is quite slow and takes a lot of memory, so I am not sure if I should add it to our test suite.

@targos
Copy link
Member

targos commented Sep 3, 2024

Thanks for demonstrating it. Let's not add the test.

@tniessen tniessen added the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2024
@github-actions github-actions bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2024
@nodejs-github-bot

This comment has been minimized.

@tniessen tniessen added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Sep 3, 2024
@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot

This comment was marked as outdated.

const char* errmsg = sqlite3_errmsg(db);
Local<String> js_msg = String::NewFromUtf8(isolate, errmsg).ToLocalChecked();
inline Local<Object> CreateSQLiteError(Isolate* isolate, const char* message) {
Local<String> js_msg = String::NewFromUtf8(isolate, message).ToLocalChecked();
Local<Object> e = Exception::Error(js_msg)
->ToObject(isolate->GetCurrentContext())
.ToLocalChecked();
e->Set(isolate->GetCurrentContext(),
Copy link
Member

Choose a reason for hiding this comment

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

I know it's not new to this PR but this should likely return MaybeLocal<Object> and not depend on ToLocalChecked() and Check(), in order to be most consistent.

@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot
Copy link
Collaborator

@tniessen tniessen added the commit-queue Add this label to land a pull request using GitHub Actions. label Sep 7, 2024
@nodejs-github-bot nodejs-github-bot removed the commit-queue Add this label to land a pull request using GitHub Actions. label Sep 7, 2024
@nodejs-github-bot nodejs-github-bot merged commit 17b49bd into nodejs:main Sep 7, 2024
70 checks passed
@nodejs-github-bot
Copy link
Collaborator

Landed in 17b49bd

aduh95 pushed a commit that referenced this pull request Sep 12, 2024
The call to sqlite3_expanded_sql() may return NULL depending on various
factors. Handle this case instead of running into a segmentation fault.

PR-URL: #54687
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
@RafaelGSS RafaelGSS mentioned this pull request Sep 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author ready PRs that have at least one approval, no pending requests for changes, and a CI started. 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.

5 participants