commit 1584d0612867b2fc0770197b036ef41090121095
parent e0f4a099a0b3905213b62623a90c5915f9f0290b
Author: Florian Dold <florian@dold.me>
Date: Sat, 8 Jul 2023 00:50:00 +0200
Revert "fix quickjs use-after-free"
This reverts commit 3061ee2130e4a08a9257e47552014be553129f9b.
Diffstat:
2 files changed, 1 insertion(+), 51 deletions(-)
diff --git a/promtest3.js b/promtest3.js
@@ -1,33 +0,0 @@
-// Running this file crashes vanilla quickjs-2021-03-27
-// due to a use-after-free bug.
-// That happens because innerFun keeps a reference
-// to the local variable p, but p gets erroneously
-// garbage-collected.
-// The outerFun infinitely waits for the promise
-// to finish. Thus, its local variables are kept on the
-// stack, and innerFun doesn't directly reference
-// outerFun, so outerFun gets collected
-// by the cycle collector.
-//
-// This seems to only happens in the cycle collector,
-// because in other scenarios, the async function
-// is finalized (and var refs are closed)
-// before the referenced values are garbage-collected.
-
-import * as os from "os";
-import * as std from "std";
-
-let f;
-
-async function outerFun() {
- let p = { foo: "bar" };
- function innerFun() {
- console.log(p.foo);
- }
- f = innerFun;
- // Wait indefinitely
- await new Promise(function promIni() {});
-}
-
-outerFun();
-os.setTimeout(() => { std.gc(); f(); }, 0);
diff --git a/quickjs/quickjs.c b/quickjs/quickjs.c
@@ -913,12 +913,11 @@ struct JSObject {
struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */
struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */
- struct { /* JS_CLASS_BYTECODE_FUNCTION: 14/32 bytes */
+ struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */
/* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */
struct JSFunctionBytecode *function_bytecode;
JSVarRef **var_refs;
JSObject *home_object; /* for 'super' access */
- JSValue outer_func; /* XXX: combine with var_refs to save space */
} func;
struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */
JSContext *realm;
@@ -5317,7 +5316,6 @@ static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val)
}
b = p->u.func.function_bytecode;
if (b) {
- JS_FreeValueRT(rt, p->u.func.outer_func);
var_refs = p->u.func.var_refs;
if (var_refs) {
for(i = 0; i < b->closure_var_count; i++)
@@ -5340,8 +5338,6 @@ static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object),
mark_func);
}
- JS_MarkValue(rt, p->u.func.outer_func,
- mark_func);
if (b) {
if (var_refs) {
for(i = 0; i < b->closure_var_count; i++) {
@@ -5438,7 +5434,6 @@ static void free_object(JSRuntime *rt, JSObject *p)
p->u.opaque = NULL;
p->u.func.var_refs = NULL;
p->u.func.home_object = NULL;
- p->u.func.outer_func = JS_UNDEFINED;
remove_gc_object(&p->header);
if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) {
@@ -15767,22 +15762,11 @@ static JSValue js_closure2(JSContext *ctx, JSValue func_obj,
p->u.func.function_bytecode = b;
p->u.func.home_object = NULL;
p->u.func.var_refs = NULL;
- p->u.func.outer_func = JS_UNDEFINED;
if (b->closure_var_count) {
var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
if (!var_refs)
goto fail;
p->u.func.var_refs = var_refs;
- if (JS_IsObject(sf->cur_func)) {
- // When the outer function is an async or generator
- // we store a reference to it, to avoid the outer
- // function from being collected before it
- // has finished and the var refs are closed.
- JSObject *fp = JS_VALUE_GET_PTR(sf->cur_func);
- if (fp->class_id != JS_CLASS_BYTECODE_FUNCTION) {
- p->u.func.outer_func = JS_DupValue(ctx, sf->cur_func);
- }
- }
for(i = 0; i < b->closure_var_count; i++) {
JSClosureVar *cv = &b->closure_var[i];
JSVarRef *var_ref;
@@ -28071,7 +28055,6 @@ static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m)
b->header.ref_count++;
p->u.func.home_object = NULL;
p->u.func.var_refs = NULL;
- p->u.func.outer_func = JS_UNDEFINED;
if (b->closure_var_count) {
var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
if (!var_refs)