Skip to content

Commit c846cb1

Browse files
committed
Fix double free of a CallSite when the backtrace array insertion fails
1 parent 7955cfd commit c846cb1

2 files changed

Lines changed: 44 additions & 3 deletions

File tree

api-test.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,43 @@ static void new_errors(void)
928928
JS_FreeRuntime(rt);
929929
}
930930

931+
static void backtrace_oom_callsite_array(void)
932+
{
933+
static const char setup_code[] =
934+
"Error.prepareStackTrace = (e, frames) => frames;\n"
935+
"globalThis.f = () => new Error();\n";
936+
JSValue global_object, func, ret;
937+
JSMemoryUsage stats;
938+
uint32_t headroom;
939+
JSRuntime *rt;
940+
JSContext *ctx;
941+
942+
rt = new_runtime();
943+
ctx = JS_NewContext(rt);
944+
global_object = JS_GetGlobalObject(ctx);
945+
946+
ret = eval(ctx, setup_code);
947+
assert(!JS_IsException(ret));
948+
JS_FreeValue(ctx, ret);
949+
950+
func = JS_GetPropertyStr(ctx, global_object, "f");
951+
assert(JS_IsFunction(ctx, func));
952+
953+
for (headroom = 0; headroom < 2048; headroom++) {
954+
JS_ComputeMemoryUsage(rt, &stats);
955+
JS_SetMemoryLimit(rt, (size_t)stats.malloc_size + headroom);
956+
ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL);
957+
JS_SetMemoryLimit(rt, 0);
958+
JS_FreeValue(ctx, ret);
959+
JS_FreeValue(ctx, JS_GetException(ctx));
960+
}
961+
962+
JS_FreeValue(ctx, func);
963+
JS_FreeValue(ctx, global_object);
964+
JS_FreeContext(ctx);
965+
JS_FreeRuntime(rt);
966+
}
967+
931968
static void backtrace_oom_current_exception(void)
932969
{
933970
static const char setup_code[] =
@@ -1372,6 +1409,7 @@ int main(void)
13721409
dump_memory_usage();
13731410
new_errors();
13741411
backtrace_oom_current_exception();
1412+
backtrace_oom_callsite_array();
13751413
global_object_prototype();
13761414
slice_string_tocstring();
13771415
immutable_array_buffer();

quickjs.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8377,10 +8377,8 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_val,
83778377
JSValue v = js_new_callsite(ctx, &csd[j]);
83788378
if (JS_IsException(v))
83798379
break;
8380-
if (JS_DefinePropertyValueUint32(ctx, stack, j, v, JS_PROP_C_W_E) < 0) {
8381-
JS_FreeValue(ctx, v);
8380+
if (JS_DefinePropertyValueUint32(ctx, stack, j, v, JS_PROP_C_W_E) < 0)
83828381
break;
8383-
}
83848382
}
83858383
}
83868384
// Clear the csd's we didn't use in case of error.
@@ -63090,7 +63088,12 @@ static JSValue js_new_callsite(JSContext *ctx, JSCallSiteData *csd) {
6309063088
return JS_EXCEPTION;
6309163089
}
6309263090

63091+
/* The new object takes ownership of the values in |csd|; clear them so
63092+
the caller doesn't free them a second time. */
6309363093
memcpy(csd1, csd, sizeof(*csd));
63094+
csd->filename = JS_UNDEFINED;
63095+
csd->func = JS_UNDEFINED;
63096+
csd->func_name = JS_UNDEFINED;
6309463097

6309563098
JS_SetOpaqueInternal(obj, csd1);
6309663099

0 commit comments

Comments
 (0)