]> git.kaiwu.me - quickjs.git/commitdiff
Prevent UB on memcpy and floating point conversions
authorCharlie Gordon <github@chqrlie.org>
Sun, 3 Mar 2024 13:05:40 +0000 (14:05 +0100)
committerCharlie Gordon <github@chqrlie.org>
Sun, 3 Mar 2024 13:05:40 +0000 (14:05 +0100)
- 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`

cutils.c
cutils.h
libbf.c
quickjs.c
quickjs.h

index b4960f9cb1bbe809c101dc115c714a0c57731e59..c0aacef69ed3d452cdcb3f6deb9de28c86631812 100644 (file)
--- a/cutils.c
+++ b/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;
 }
index ff2d3fb62a6f5cb962175d7f4c30fead9a4b7b52..11246e3ce30eac35dcfd238f50f20cb80ef6003e 100644 (file)
--- a/cutils.h
+++ b/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/libbf.c b/libbf.c
index b3ed2094621b50449edf071592390f1a8a0617e9..dee394ac759258bafedf27e00499b5fe99f8f4cc 100644 (file)
--- a/libbf.c
+++ b/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;
 }
 
index 6e1bb366096f26bbf31fe2329ffdce2f3c777927..ebf45a988c13036f6ceda09ce1da390662dd1179 100644 (file)
--- a/quickjs.c
+++ b/quickjs.c
@@ -11078,6 +11078,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;
@@ -33388,8 +33390,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;
@@ -53997,9 +53999,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]);
 
index 003af2fb57297adbf84ef1ec5a3528af4e2c4fa3..a951e675d40cc6db0b5d45a04990460d08389efe 100644 (file)
--- a/quickjs.h
+++ b/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)