commit 3a9af1cb8e23c90149550dd15d91e064882bfffb
parent 63b0b8e6d0a1e0d5f0b8d36e02bac66daa2d1101
Author: Charlie Gordon <github@chqrlie.org>
Date: Sun, 3 Mar 2024 14:05:40 +0100
Prevent UB on memcpy and floating point conversions
- add `memcpy_no_ub` that accepts null pointers for 0 count
- prevent 0 length allocation in `js_worker_postMessage`
- use safer test for `int` value in `JS_NewFloat64`,
`JS_ToArrayLengthFree` and `js_typed_array_indexOf`
Diffstat:
5 files changed, 26 insertions(+), 18 deletions(-)
diff --git a/quickjs/cutils.c b/quickjs/cutils.c
@@ -140,7 +140,7 @@ int dbuf_put(DynBuf *s, const uint8_t *data, size_t len)
if (dbuf_realloc(s, s->size + len))
return -1;
}
- memcpy(s->buf + s->size, data, len);
+ memcpy_no_ub(s->buf + s->size, data, len);
s->size += len;
return 0;
}
diff --git a/quickjs/cutils.h b/quickjs/cutils.h
@@ -26,6 +26,7 @@
#define CUTILS_H
#include <stdlib.h>
+#include <string.h>
#include <inttypes.h>
#define likely(x) __builtin_expect(!!(x), 1)
@@ -64,6 +65,12 @@ char *pstrcat(char *buf, int buf_size, const char *s);
int strstart(const char *str, const char *val, const char **ptr);
int has_suffix(const char *str, const char *suffix);
+/* Prevent UB when n == 0 and (src == NULL or dest == NULL) */
+static inline void memcpy_no_ub(void *dest, const void *src, size_t n) {
+ if (n)
+ memcpy(dest, src, n);
+}
+
static inline int max_int(int a, int b)
{
if (a > b)
diff --git a/quickjs/libbf.c b/quickjs/libbf.c
@@ -309,7 +309,7 @@ int bf_set(bf_t *r, const bf_t *a)
}
r->sign = a->sign;
r->expn = a->expn;
- memcpy(r->tab, a->tab, a->len * sizeof(limb_t));
+ memcpy_no_ub(r->tab, a->tab, a->len * sizeof(limb_t));
return 0;
}
diff --git a/quickjs/quickjs.c b/quickjs/quickjs.c
@@ -11093,6 +11093,8 @@ static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
if (JS_TAG_IS_FLOAT64(tag)) {
double d;
d = JS_VALUE_GET_FLOAT64(val);
+ if (!(d >= 0 && d <= UINT32_MAX))
+ goto fail;
len = (uint32_t)d;
if (len != d)
goto fail;
@@ -33575,8 +33577,8 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
}
} else {
b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
- memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0]));
- memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0]));
+ memcpy_no_ub(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0]));
+ memcpy_no_ub(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0]));
}
b->var_count = fd->var_count;
b->arg_count = fd->arg_count;
@@ -54273,9 +54275,10 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
} else
if (tag == JS_TAG_FLOAT64) {
d = JS_VALUE_GET_FLOAT64(argv[0]);
- // XXX: should fix UB
- v64 = d;
- is_int = (v64 == d);
+ if (d >= INT64_MIN && d < 0x1p63) {
+ v64 = d;
+ is_int = (v64 == d);
+ }
} else if (tag == JS_TAG_BIG_INT) {
JSBigFloat *p1 = JS_VALUE_GET_PTR(argv[0]);
diff --git a/quickjs/quickjs.h b/quickjs/quickjs.h
@@ -550,23 +550,21 @@ JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v);
static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d)
{
- JSValue v;
int32_t val;
union {
double d;
uint64_t u;
} u, t;
- u.d = d;
- val = (int32_t)d;
- t.d = val;
- /* -0 cannot be represented as integer, so we compare the bit
- representation */
- if (u.u == t.u) {
- v = JS_MKVAL(JS_TAG_INT, val);
- } else {
- v = __JS_NewFloat64(ctx, d);
+ if (d >= INT32_MIN && d <= INT32_MAX) {
+ u.d = d;
+ val = (int32_t)d;
+ t.d = val;
+ /* -0 cannot be represented as integer, so we compare the bit
+ representation */
+ if (u.u == t.u)
+ return JS_MKVAL(JS_TAG_INT, val);
}
- return v;
+ return __JS_NewFloat64(ctx, d);
}
static inline JS_BOOL JS_IsNumber(JSValueConst v)