Skip to content

Commit 4469082

Browse files
committed
FIX: preserve embedded NUL bytes
Keep JavaScript exception messages and dotted function names intact when they contain NUL bytes instead of truncating at C string boundaries. Reject V8 flags with NUL bytes or overly long values, validate callback ids, and cover edge cases for large integer serialization.
1 parent 60a1e4d commit 4469082

5 files changed

Lines changed: 324 additions & 83 deletions

File tree

CHANGELOG

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- Fix native memory leaks in `Context#heap_snapshot`/`Context#write_heap_snapshot`; thanks to Pranjali Thakur from depthfirst.com
44
- Fix large integral JavaScript numbers wrapping to negative Ruby integers; thanks to Pranjali Thakur from depthfirst.com
55
- Fix Ruby callback exceptions with embedded NUL bytes permanently deadlocking a context; thanks to Pranjali Thakur from depthfirst.com
6+
- Preserve embedded NUL bytes in JavaScript exception messages and attached function names, and reject unsafe V8 flags containing NUL bytes or overly long values
67

78
- 0.21.1 - 25-05-2026
89
- Run `:single_threaded` V8 dispatches on a reusable mini_racer-owned native thread so V8 does not execute on Ruby-owned threads

ext/mini_racer_extension/mini_racer_extension.c

Lines changed: 72 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -370,19 +370,8 @@ static void des_bigint(void *arg, const void *p, size_t n, int sign)
370370
if (t >> 63)
371371
*a++ = 0; // suppress sign extension
372372
v = rb_big_unpack(limbs, a-limbs);
373-
if (sign < 0) {
374-
// rb_big_unpack returns T_FIXNUM for smallish bignums
375-
switch (TYPE(v)) {
376-
case T_BIGNUM:
377-
v = rb_big_mul(v, LONG2FIX(-1));
378-
break;
379-
case T_FIXNUM:
380-
v = LONG2FIX(-1 * FIX2LONG(v));
381-
break;
382-
default:
383-
abort();
384-
}
385-
}
373+
if (sign < 0)
374+
v = rb_funcall(v, rb_intern("-@"), 0);
386375
put(c, v);
387376
}
388377

@@ -943,6 +932,7 @@ static VALUE rendezvous_callback_do(VALUE arg)
943932
Context *c;
944933
DesCtx d;
945934
Buf *b;
935+
long id;
946936

947937
a = (void *)arg;
948938
b = a->res;
@@ -952,7 +942,12 @@ static VALUE rendezvous_callback_do(VALUE arg)
952942
DesCtx_init(&d);
953943
args = deserialize1(&d, b->buf+1, b->len-1); // skip 'c' marker
954944
func = rb_ary_pop(args); // callback id
955-
func = rb_ary_entry(c->procs, FIX2LONG(func));
945+
if (!RB_INTEGER_TYPE_P(func))
946+
rb_raise(runtime_error, "bad callback id");
947+
id = NUM2LONG(func);
948+
if (id < 0 || id >= RARRAY_LEN(c->procs))
949+
rb_raise(runtime_error, "bad callback id");
950+
func = rb_ary_entry(c->procs, id);
956951
return rb_funcall2(func, rb_intern("call"), RARRAY_LENINT(args), RARRAY_PTR(args));
957952
}
958953

@@ -1122,16 +1117,35 @@ static VALUE rendezvous(Context *c, Buf *req)
11221117
return rendezvous1(c, req, &d);
11231118
}
11241119

1120+
static void raise_exception_with_message(VALUE klass, VALUE e)
1121+
{
1122+
long n;
1123+
VALUE message;
1124+
1125+
if (NIL_P(e))
1126+
return;
1127+
e = StringValue(e);
1128+
n = RSTRING_LEN(e);
1129+
if (n == 0 || RSTRING_PTR(e)[0] == NO_ERROR)
1130+
return;
1131+
message = rb_str_subseq(e, 1, n - 1);
1132+
rb_exc_raise(rb_exc_new_str(klass, message));
1133+
}
1134+
11251135
static void handle_exception(VALUE e)
11261136
{
11271137
const char *s;
11281138
VALUE klass;
1139+
long n;
11291140

11301141
if (NIL_P(e))
11311142
return;
11321143
e = StringValue(e);
1133-
s = StringValueCStr(e);
1134-
switch (*s) {
1144+
n = RSTRING_LEN(e);
1145+
if (n == 0)
1146+
return;
1147+
s = RSTRING_PTR(e);
1148+
switch ((unsigned char)*s) {
11351149
case NO_ERROR:
11361150
return;
11371151
case INTERNAL_ERROR:
@@ -1150,9 +1164,9 @@ static void handle_exception(VALUE e)
11501164
klass = terminated_error;
11511165
break;
11521166
default:
1153-
rb_raise(internal_error, "bad error class %02x", *s);
1167+
rb_raise(internal_error, "bad error class %02x", (unsigned char)*s);
11541168
}
1155-
rb_enc_raise(rb_enc_get(e), klass, "%s", s+1);
1169+
raise_exception_with_message(klass, e);
11561170
}
11571171

11581172
static VALUE context_alloc(VALUE klass)
@@ -1324,13 +1338,17 @@ static VALUE context_attach(VALUE self, VALUE name, VALUE proc)
13241338
Context *c;
13251339
VALUE e;
13261340
Ser s;
1341+
long id;
13271342

13281343
TypedData_Get_Struct(self, Context, &context_type, c);
1344+
id = RARRAY_LEN(c->procs);
1345+
if (id > INT32_MAX)
1346+
rb_raise(runtime_error, "too many callbacks");
13291347
// request is (A)ttach, [name, id] array
13301348
ser_init1(&s, 'A');
13311349
ser_array_begin(&s, 2);
13321350
add_string(&s, name);
1333-
ser_int(&s, RARRAY_LENINT(c->procs));
1351+
ser_int(&s, id);
13341352
ser_array_end(&s, 2);
13351353
rb_ary_push(c->procs, proc);
13361354
// response is an exception or undefined
@@ -1528,7 +1546,8 @@ static VALUE context_low_memory_notification(VALUE self)
15281546

15291547
static int platform_set_flag1(VALUE k, VALUE v)
15301548
{
1531-
char *p, *q, buf[256];
1549+
char *p, *q, *r, buf[256];
1550+
long pn, vn, len;
15321551
int ok;
15331552

15341553
k = rb_funcall(k, rb_intern("to_s"), 0);
@@ -1538,12 +1557,40 @@ static int platform_set_flag1(VALUE k, VALUE v)
15381557
Check_Type(v, T_STRING);
15391558
}
15401559
p = RSTRING_PTR(k);
1541-
if (!strncmp(p, "--", 2))
1560+
pn = RSTRING_LEN(k);
1561+
if (memchr(p, '\0', pn))
1562+
rb_raise(rb_eArgError, "flag contains NUL byte");
1563+
if (pn >= 2 && p[0] == '-' && p[1] == '-') {
15421564
p += 2;
1565+
pn -= 2;
1566+
}
15431567
if (NIL_P(v)) {
1544-
snprintf(buf, sizeof(buf), "--%s", p);
1568+
len = 2 + pn;
1569+
if (len >= (long)sizeof(buf))
1570+
rb_raise(rb_eArgError, "flag too long");
1571+
q = buf;
1572+
*q++ = '-';
1573+
*q++ = '-';
1574+
memcpy(q, p, pn);
1575+
q += pn;
1576+
*q = '\0';
15451577
} else {
1546-
snprintf(buf, sizeof(buf), "--%s=%s", p, RSTRING_PTR(v));
1578+
q = RSTRING_PTR(v);
1579+
vn = RSTRING_LEN(v);
1580+
if (memchr(q, '\0', vn))
1581+
rb_raise(rb_eArgError, "flag contains NUL byte");
1582+
len = 3 + pn + vn;
1583+
if (len >= (long)sizeof(buf))
1584+
rb_raise(rb_eArgError, "flag too long");
1585+
r = buf;
1586+
*r++ = '-';
1587+
*r++ = '-';
1588+
memcpy(r, p, pn);
1589+
r += pn;
1590+
*r++ = '=';
1591+
memcpy(r, q, vn);
1592+
r += vn;
1593+
*r = '\0';
15471594
}
15481595
p = buf;
15491596
pthread_mutex_lock(&flags_mtx);
@@ -1756,8 +1803,7 @@ static VALUE snapshot_initialize(int argc, VALUE *argv, VALUE self)
17561803
a = rendezvous1(c, &s.b, &d);
17571804
e = rb_ary_pop(a);
17581805
context_dispose(cv);
1759-
if (*RSTRING_PTR(e))
1760-
rb_raise(snapshot_error, "%s", RSTRING_PTR(e)+1);
1806+
raise_exception_with_message(snapshot_error, e);
17611807
ss->blob = rb_ary_pop(a);
17621808
return Qnil;
17631809
}
@@ -1787,8 +1833,7 @@ static VALUE snapshot_warmup(VALUE self, VALUE arg)
17871833
a = rendezvous1(c, &s.b, &d);
17881834
e = rb_ary_pop(a);
17891835
context_dispose(cv);
1790-
if (*RSTRING_PTR(e))
1791-
rb_raise(snapshot_error, "%s", RSTRING_PTR(e)+1);
1836+
raise_exception_with_message(snapshot_error, e);
17921837
ss->blob = rb_ary_pop(a);
17931838
return self;
17941839
}

0 commit comments

Comments
 (0)