Skip to content

Commit 128f247

Browse files
authored
sqlite: use 64-bit result APIs for UDF return values
JSValueToSQLiteResult() passed the size_t length of returned ArrayBuffers and ArrayBufferViews to sqlite3_result_blob(), which takes an int. Buffers larger than INT_MAX bytes were narrowed, yielding silently truncated blobs or bogus empty text values. Use sqlite3_result_blob64() and sqlite3_result_text64(), matching Statement::BindValue(), so oversized values are rejected with SQLITE_TOOBIG. Assisted-by: Claude Code Signed-off-by: Guilherme Araújo <arauujogui@gmail.com> PR-URL: #67089 Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
1 parent 280798a commit 128f247

2 files changed

Lines changed: 26 additions & 3 deletions

File tree

‎src/node_sqlite.cc‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,11 +332,12 @@ void JSValueToSQLiteResult(Isolate* isolate,
332332
sqlite3_result_double(ctx, value.As<Number>()->Value());
333333
} else if (value->IsString()) {
334334
Utf8Value val(isolate, value.As<String>());
335-
sqlite3_result_text(ctx, *val, val.length(), SQLITE_TRANSIENT);
335+
sqlite3_result_text64(
336+
ctx, *val, val.length(), SQLITE_TRANSIENT, SQLITE_UTF8);
336337
} else if (value->IsArrayBufferView() || value->IsArrayBuffer() ||
337338
value->IsSharedArrayBuffer()) {
338339
ArrayBufferViewContents<uint8_t> buf(value);
339-
sqlite3_result_blob(ctx, buf.data(), buf.length(), SQLITE_TRANSIENT);
340+
sqlite3_result_blob64(ctx, buf.data(), buf.length(), SQLITE_TRANSIENT);
340341
} else if (value->IsBigInt()) {
341342
bool lossless;
342343
int64_t as_int = value.As<BigInt>()->Int64Value(&lossless);

‎test/parallel/test-sqlite-custom-functions.js‎

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use strict';
2-
const { skipIfSQLiteMissing, mustCall } = require('../common');
2+
const { skipIfSQLiteMissing, mustCall, enoughTestMem } = require('../common');
33
skipIfSQLiteMissing();
44
const assert = require('node:assert');
55
const { Database } = require('node:sqlite');
@@ -347,6 +347,28 @@ suite('Database.prototype.function()', () => {
347347
});
348348
});
349349

350+
test('throws if returned buffer exceeds INT_MAX bytes', {
351+
skip: !enoughTestMem && 'insufficient memory',
352+
}, (t) => {
353+
const db = new Database(':memory:');
354+
for (const size of [2 ** 31, 2 ** 32 + 5]) {
355+
let buf;
356+
try {
357+
buf = new ArrayBuffer(size);
358+
} catch {
359+
return t.skip('insufficient memory');
360+
}
361+
db.function('retLargeBuffer', () => buf);
362+
const stmt = db.prepare('SELECT retLargeBuffer() AS retLargeBuffer');
363+
assert.throws(() => {
364+
stmt.get();
365+
}, {
366+
code: 'ERR_SQLITE_ERROR',
367+
message: /string or blob too big/,
368+
});
369+
}
370+
});
371+
350372
test('throws on unsupported return types', () => {
351373
const db = new Database(':memory:');
352374
db.function('retFunction', () => {

0 commit comments

Comments
 (0)