commit 201c563e2906b184aab8ea4490289d31fc861c03
parent c9c4c204176a07c1a67d2bad27e20f5853c104d7
Author: Florian Dold <dold@taler.net>
Date: Sun, 9 Aug 2026 21:07:42 +0200
tart: validate native buffer allocations
Diffstat:
| M | tart_module.c | | | 81 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- |
1 file changed, 72 insertions(+), 9 deletions(-)
diff --git a/tart_module.c b/tart_module.c
@@ -35,6 +35,8 @@
#include "sqlite3/sqlite3.h"
#endif
+static JSValue make_js_ta_copy(JSContext *ctx, uint8_t *data, size_t size);
+
static JSValue js_encode_utf8(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
@@ -42,6 +44,9 @@ static JSValue js_encode_utf8(JSContext *ctx, JSValueConst this_val,
size_t len;
JSValue buf;
str = JS_ToCStringLen2(ctx, &len, argv[0], FALSE);
+ if (!str) {
+ return JS_EXCEPTION;
+ }
// FIXME: Don't copy buffer but pass destructor function
buf = JS_NewArrayBufferCopy(ctx, (const uint8_t*) str, len);
JS_FreeCString(ctx, str);
@@ -59,14 +64,23 @@ static JSValue js_random_bytes(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
uint32_t nbytes;
- JSValue buf;
+ uint8_t *randbuf = NULL;
+ JSValue buf = JS_EXCEPTION;
+
if (0 != JS_ToUint32(ctx, &nbytes, argv[0])) {
return JS_EXCEPTION;
}
- {
- uint8_t randbuf[nbytes];
+ if (nbytes != 0) {
+ randbuf = js_malloc(ctx, nbytes);
+ if (!randbuf) {
+ return JS_EXCEPTION;
+ }
randombytes_buf(randbuf, nbytes);
- buf = JS_NewArrayBufferCopy(ctx, randbuf, nbytes);
+ }
+ buf = JS_NewArrayBufferCopy(ctx, randbuf, nbytes);
+ if (randbuf) {
+ sodium_memzero(randbuf, nbytes);
+ js_free(ctx, randbuf);
}
return buf;
}
@@ -151,7 +165,9 @@ static JSValue js_talercrypto_encode_crock(JSContext *ctx, JSValue this_val,
goto exception;
}
- assert (size < SIZE_MAX / 8 - 4);
+ if (size > (SIZE_MAX - 4) / 8) {
+ return JS_ThrowRangeError(ctx, "input is too large");
+ }
out_size = size * 8;
if (out_size % 5 > 0)
@@ -159,10 +175,11 @@ static JSValue js_talercrypto_encode_crock(JSContext *ctx, JSValue this_val,
out_size /= 5;
out = malloc (out_size + 1);
- memset (out, 0, out_size + 1);
if (!out) {
+ JS_ThrowOutOfMemory(ctx);
goto exception;
}
+ memset (out, 0, out_size + 1);
udata = buf;
if (out_size < (size * 8 + 4) / 5) {
@@ -232,10 +249,21 @@ static JSValue js_talercrypto_decode_crock(JSContext *ctx, JSValue this_val,
goto exception;
}
+ if (enclen == 0) {
+ ret_val = make_js_ta_copy(ctx, NULL, 0);
+ goto done;
+ }
+ if (enclen > SIZE_MAX / 5) {
+ JS_ThrowRangeError(ctx, "encoded input is too large");
+ goto exception;
+ }
out_size = (enclen * 5) / 8;
encoded_len = out_size * 8;
uout = malloc(out_size);
- assert (out_size < SIZE_MAX / 8);
+ if (out_size != 0 && !uout) {
+ JS_ThrowOutOfMemory(ctx);
+ goto exception;
+ }
wpos = out_size;
rpos = enclen;
if ((encoded_len % 5) > 0)
@@ -267,6 +295,7 @@ static JSValue js_talercrypto_decode_crock(JSContext *ctx, JSValue this_val,
}
bits = ((ret = getValue__ (enc[--rpos])) << vbit) | bits;
if (-1 == ret) {
+ JS_ThrowTypeError(ctx, "invalid character in encoding");
goto exception;
}
vbit += 5;
@@ -514,14 +543,32 @@ static JSValue js_talercrypto_hash_argon2id(JSContext *ctx, JSValue this_val,
goto exception;
}
+ if (iters < crypto_pwhash_OPSLIMIT_MIN ||
+ iters > crypto_pwhash_OPSLIMIT_MAX) {
+ JS_ThrowRangeError(ctx, "iterations out of range");
+ goto exception;
+ }
+ if (hash_len < crypto_pwhash_BYTES_MIN ||
+ hash_len > crypto_pwhash_BYTES_MAX) {
+ JS_ThrowRangeError(ctx, "hash length out of range");
+ goto exception;
+ }
+
// Check for overflow when converting memory size to bytes
if (((unsigned long long)mem_size * 1024) > UINT32_MAX) {
JS_ThrowTypeError(ctx, "mem_size too large");
goto exception;
}
+ if ((size_t) mem_size * 1024 < crypto_pwhash_MEMLIMIT_MIN ||
+ (size_t) mem_size * 1024 > crypto_pwhash_MEMLIMIT_MAX) {
+ JS_ThrowRangeError(ctx, "memory size out of range");
+ goto exception;
+ }
+
hash = malloc(hash_len);
if (NULL == hash) {
+ JS_ThrowOutOfMemory(ctx);
goto exception;
}
@@ -727,6 +774,7 @@ static JSValue js_talercrypto_kdf(JSContext *ctx, JSValue this_val,
uint8_t *ikm;
uint8_t *info;
uint8_t *okm = NULL;
+ uint8_t empty_okm;
uint32_t out_bytes;
JSValue ret_val;
int ret;
@@ -735,6 +783,10 @@ static JSValue js_talercrypto_kdf(JSContext *ctx, JSValue this_val,
goto exception;
}
okm_len = out_bytes;
+ if (okm_len > 255 * 32) {
+ JS_ThrowRangeError(ctx, "HKDF output is too large");
+ goto exception;
+ }
ikm = JS_GetArrayBuffer(ctx, &ikm_len, argv[1]);
if (!ikm) {
@@ -761,9 +813,16 @@ static JSValue js_talercrypto_kdf(JSContext *ctx, JSValue this_val,
}
}
- okm = malloc(okm_len);
+ if (okm_len != 0) {
+ okm = malloc(okm_len);
+ if (!okm) {
+ JS_ThrowOutOfMemory(ctx);
+ goto exception;
+ }
+ }
- ret = kdf(okm, okm_len, ikm, ikm_len, salt, salt_len, info, info_len);
+ ret = kdf(okm_len == 0 ? &empty_okm : okm, okm_len,
+ ikm, ikm_len, salt, salt_len, info, info_len);
if (ret != 0) {
JS_ThrowInternalError(ctx, "kdf() call failed");
@@ -1390,6 +1449,10 @@ static JSValue js_talercrypto_hash_state_update(JSContext *ctx, JSValue this_val
data = JS_GetArrayBuffer(ctx, &data_len, data_val);
+ if (!data) {
+ return JS_EXCEPTION;
+ }
+
if (0 != crypto_hash_sha512_update(&hstate->h, data, data_len)) {
return JS_ThrowInternalError(ctx, "hashing failed");
}