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:
| M | tart_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));