Skip to content

Commit e1c1e41

Browse files
mvanhornsaghul
authored andcommitted
fix: heap-use-after-free in build_backtrace when dbuf OOM frees current_exception
If JS_NewError() during build_backtrace triggered dbuf OOM, JS_ThrowOutOfMemory freed the current exception (error_val from the caller's stack frame), then the rest of build_backtrace continued using the freed error_val for the prepareStackTrace call and the JS_DefinePropertyValue of the stack property. The fix duplicates error_val into a local error_obj at function entry, uses error_obj throughout the function, and frees it at exit. Fixes #1469
1 parent 9584b58 commit e1c1e41

2 files changed

Lines changed: 39 additions & 5 deletions

File tree

api-test.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,37 @@ static void new_errors(void)
776776
JS_FreeRuntime(rt);
777777
}
778778

779+
static void backtrace_oom_current_exception(void)
780+
{
781+
static const char setup_code[] =
782+
"globalThis.f = function() { missing; };\n"
783+
"Object.defineProperty(f, 'name', { value: 'x'.repeat(2 * 1024 * 1024) });";
784+
JSMemoryUsage stats;
785+
JSValue ret, exception;
786+
JSRuntime *rt;
787+
JSContext *ctx;
788+
789+
rt = new_runtime();
790+
ctx = JS_NewContext(rt);
791+
792+
ret = eval(ctx, setup_code);
793+
assert(!JS_IsException(ret));
794+
JS_FreeValue(ctx, ret);
795+
796+
JS_ComputeMemoryUsage(rt, &stats);
797+
JS_SetMemoryLimit(rt, (size_t)stats.malloc_size + 128 * 1024);
798+
799+
ret = eval(ctx, "f()");
800+
assert(JS_IsException(ret));
801+
assert(JS_HasException(ctx));
802+
exception = JS_GetException(ctx);
803+
JS_FreeValue(ctx, exception);
804+
JS_SetMemoryLimit(rt, 0);
805+
806+
JS_FreeContext(ctx);
807+
JS_FreeRuntime(rt);
808+
}
809+
779810
static int gop_get_own_property(JSContext *ctx, JSPropertyDescriptor *desc,
780811
JSValueConst obj, JSAtom prop)
781812
{
@@ -1053,6 +1084,7 @@ int main(void)
10531084
promise_hook();
10541085
dump_memory_usage();
10551086
new_errors();
1087+
backtrace_oom_current_exception();
10561088
global_object_prototype();
10571089
slice_string_tocstring();
10581090
immutable_array_buffer();

quickjs.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7762,7 +7762,7 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_val,
77627762
int line_num, int col_num, int backtrace_flags)
77637763
{
77647764
JSStackFrame *sf, *sf_start;
7765-
JSValue stack, prepare, saved_exception;
7765+
JSValue stack, prepare, saved_exception, error_obj;
77667766
DynBuf dbuf;
77677767
const char *func_name_str;
77687768
const char *str1;
@@ -7779,6 +7779,7 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_val,
77797779
if (rt->in_build_stack_trace)
77807780
return;
77817781
rt->in_build_stack_trace = true;
7782+
error_obj = js_dup(error_val);
77827783

77837784
// Save exception because conversion to double may fail.
77847785
saved_exception = JS_GetException(ctx);
@@ -7924,7 +7925,7 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_val,
79247925
JS_FreeValue(ctx, csd[k].func_name);
79257926
}
79267927
JSValueConst args[] = {
7927-
error_val,
7928+
error_obj,
79287929
stack,
79297930
};
79307931
JSValue stack2 = JS_Call(ctx, prepare, ctx->error_ctor, countof(args), args);
@@ -7945,13 +7946,14 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_val,
79457946

79467947
if (JS_IsUndefined(ctx->error_back_trace))
79477948
ctx->error_back_trace = js_dup(stack);
7948-
if (has_filter_func || can_add_backtrace(error_val)) {
7949-
JS_DefinePropertyValue(ctx, error_val, JS_ATOM_stack, stack,
7949+
if (has_filter_func || can_add_backtrace(error_obj)) {
7950+
JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, stack,
79507951
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
79517952
} else {
79527953
JS_FreeValue(ctx, stack);
79537954
}
79547955

7956+
JS_FreeValue(ctx, error_obj);
79557957
rt->in_build_stack_trace = false;
79567958
}
79577959

@@ -40234,7 +40236,7 @@ JSValue JS_ToObject(JSContext *ctx, JSValueConst val)
4023440236
if (!JS_IsException(obj)) {
4023540237
JS_DefinePropertyValue(ctx, obj, JS_ATOM_length,
4023640238
JS_NewInt32(ctx, JS_VALUE_GET_STRING(str)->len), 0);
40237-
JS_SetObjectData(ctx, obj, JS_DupValue(ctx, str));
40239+
JS_SetObjectData(ctx, obj, js_dup(str));
4023840240
}
4023940241
JS_FreeValue(ctx, str);
4024040242
return obj;

0 commit comments

Comments
 (0)