quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

commit 3e9a2ad56852144b243254f0f5adb1eaf132f76b
parent add82df147abf984d7494f62f9908c1384feb3fa
Author: Florian Dold <dold@taler.net>
Date:   Sun,  9 Aug 2026 21:09:39 +0200

tart: make native handles runtime-safe

Diffstat:
Mtart_module.c | 97++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 77 insertions(+), 20 deletions(-)

diff --git a/tart_module.c b/tart_module.c @@ -26,6 +26,7 @@ #include <ctype.h> #include <string.h> #include <assert.h> +#include <pthread.h> #include <arpa/inet.h> @@ -1470,6 +1471,9 @@ static JSValue js_talercrypto_hash_state_init(JSContext *ctx, JSValue this_val, } hstate->finalized = FALSE; obj = JS_NewObjectClass(ctx, js_hash_state_class_id); + if (JS_IsException(obj)) { + goto done; + } crypto_hash_sha512_init(&hstate->h); JS_SetOpaque(obj, hstate); hstate = NULL; @@ -1763,6 +1767,11 @@ static JSValue js_sqlite3_open(JSContext *ctx, JSValue this_val, goto done; } db_obj = JS_NewObjectClass(ctx, js_sqlite3_database_class_id); + if (JS_IsException(db_obj)) { + (void) sqlite3_close_v2(sqlite3_db); + ret_val = JS_EXCEPTION; + goto done; + } JS_SetOpaque(db_obj, sqlite3_db); ret_val = db_obj; done: @@ -1822,6 +1831,11 @@ static JSValue js_sqlite3_prepare(JSContext *ctx, JSValue this_val, } stmt_obj = JS_NewObjectClass(ctx, js_sqlite3_statement_class_id); + if (JS_IsException(stmt_obj)) { + sqlite3_finalize(stmt); + ret_val = JS_EXCEPTION; + goto done; + } JS_SetOpaque(stmt_obj, stmt); ret_val = stmt_obj; done: @@ -1883,10 +1897,14 @@ exception: goto done; } -static int find_param_index(sqlite3_stmt *stmt, const char *name) +static int find_param_index(JSContext *ctx, sqlite3_stmt *stmt, const char *name) { int param_index; char *prefixed_name = malloc(strlen(name) + 2); + if (!prefixed_name) { + JS_ThrowOutOfMemory(ctx); + return -1; + } memcpy(prefixed_name + 1, name, strlen(name) + 1); prefixed_name[0] = '$'; @@ -1911,7 +1929,7 @@ done: static int bind_from_object(JSContext *ctx, sqlite3_stmt *stmt, JSValueConst obj) { - JSValue val; + JSValue val = JS_UNDEFINED; int i; uint32_t len = 0; /* len of property table */ JSPropertyEnum *tab; @@ -1937,7 +1955,10 @@ static int bind_from_object(JSContext *ctx, sqlite3_stmt *stmt, JSValueConst obj if (!key) { goto fail; } - param_index = find_param_index(stmt, key); + param_index = find_param_index(ctx, stmt, key); + if (param_index < 0) { + goto fail; + } if (0 == param_index) { // JS_ThrowTypeError(ctx, "unable to bind, named param '%s' not found", key); //goto fail; @@ -1945,20 +1966,35 @@ static int bind_from_object(JSContext *ctx, sqlite3_stmt *stmt, JSValueConst obj goto next; } if (JS_IsNull(val)) { - sqlite3_bind_null(stmt, param_index); + if (SQLITE_OK != sqlite3_bind_null(stmt, param_index)) { + goto sqlite_fail; + } goto next; } if (JS_IsString(val)) { const char *cstr; - cstr = JS_ToCString(ctx, val); - sqlite3_bind_text(stmt, param_index, cstr, (int) strlen(cstr), SQLITE_TRANSIENT); + size_t cstr_len; + cstr = JS_ToCStringLen(ctx, &cstr_len, val); + if (!cstr) { + goto fail; + } + if (SQLITE_OK != sqlite3_bind_text64(stmt, param_index, cstr, + cstr_len, SQLITE_TRANSIENT, + SQLITE_UTF8)) { + JS_FreeCString(ctx, cstr); + goto sqlite_fail; + } JS_FreeCString(ctx, cstr); goto next; } if (JS_IsNumber(val)) { int64_t n; - JS_ToInt64(ctx, &n, val); - sqlite3_bind_int64(stmt, param_index, n); + if (0 != JS_ToInt64(ctx, &n, val)) { + goto fail; + } + if (SQLITE_OK != sqlite3_bind_int64(stmt, param_index, n)) { + goto sqlite_fail; + } goto next; } if (JS_IsArrayBuffer(val)) { @@ -1968,14 +2004,19 @@ static int bind_from_object(JSContext *ctx, sqlite3_stmt *stmt, JSValueConst obj if (!data) { goto fail; } - sqlite3_bind_blob(stmt, param_index, data, (int) size, SQLITE_TRANSIENT); + if (SQLITE_OK != sqlite3_bind_blob64(stmt, param_index, data, size, + SQLITE_TRANSIENT)) { + goto sqlite_fail; + } goto next; } JS_ThrowTypeError(ctx, "unable to bind, unsupported type for arg %s", key); goto fail; next: JS_FreeCString(ctx, key); + key = NULL; JS_FreeValue(ctx, val); + val = JS_UNDEFINED; } done: for (i = 0; i < len; i++) { @@ -1984,8 +2025,13 @@ done: js_free(ctx, tab); return retval; fail: + JS_FreeCString(ctx, key); + JS_FreeValue(ctx, val); retval = -1; goto done; +sqlite_fail: + throw_sqlite3_error(ctx, sqlite3_db_handle(stmt)); + goto fail; } @@ -2095,7 +2141,9 @@ static int extract_result_row(JSContext *ctx, sqlite3_stmt *stmt, JSValueConst t break; case SQLITE_TEXT: { const char *text = (const char *) sqlite3_column_text(stmt, i); - JS_SetPropertyStr(ctx, target, colname, JS_NewString(ctx, text)); + size_t text_len = sqlite3_column_bytes(stmt, i); + JS_SetPropertyStr(ctx, target, colname, + JS_NewStringLen(ctx, text, text_len)); break; } default: @@ -2277,19 +2325,28 @@ static const JSCFunctionListEntry tart_talercrypto_funcs[] = { JS_CFUNC_DEF("sqlite3StmtGetAll", 2, js_sqlite3_stmt_get_all), }; -static int tart_talercrypto_init(JSContext *ctx, JSModuleDef *m) -{ - /* create the HashState class */ - JS_NewClassID(&js_hash_state_class_id); - JS_NewClass(JS_GetRuntime(ctx), js_hash_state_class_id, &js_hash_state_class); +static pthread_once_t tart_class_ids_once = PTHREAD_ONCE_INIT; - /* create the Sqlite3Database class*/ +static void tart_init_class_ids(void) +{ + JS_NewClassID(&js_hash_state_class_id); JS_NewClassID(&js_sqlite3_database_class_id); - JS_NewClass(JS_GetRuntime(ctx), js_sqlite3_database_class_id, &js_sqlite3_database_class); - - /* create the Sqlite3Statement class*/ JS_NewClassID(&js_sqlite3_statement_class_id); - JS_NewClass(JS_GetRuntime(ctx), js_sqlite3_statement_class_id, &js_sqlite3_statement_class); +} + +static int tart_talercrypto_init(JSContext *ctx, JSModuleDef *m) +{ + if (0 != pthread_once(&tart_class_ids_once, tart_init_class_ids)) { + return -1; + } + if (0 != JS_NewClass(JS_GetRuntime(ctx), js_hash_state_class_id, + &js_hash_state_class) || + 0 != JS_NewClass(JS_GetRuntime(ctx), js_sqlite3_database_class_id, + &js_sqlite3_database_class) || + 0 != JS_NewClass(JS_GetRuntime(ctx), js_sqlite3_statement_class_id, + &js_sqlite3_statement_class)) { + return -1; + } return JS_SetModuleExportList(ctx, m, tart_talercrypto_funcs, countof(tart_talercrypto_funcs));