Fix use-after-free in ArrayBuffer.prototype.transfer (bnoordhuis) (#450) - use js_array_buffer_update_typed_arrays() in JS_DetachArrayBuffer()

This commit is contained in:
Fabrice Bellard
2025-11-03 18:23:19 +01:00
parent baa186fc6e
commit 75b5230000
2 changed files with 49 additions and 41 deletions

View File

@@ -55344,10 +55344,47 @@ static JSValue js_array_buffer_get_resizable(JSContext *ctx,
return JS_NewBool(ctx, array_buffer_is_resizable(abuf)); return JS_NewBool(ctx, array_buffer_is_resizable(abuf));
} }
static void js_array_buffer_update_typed_arrays(JSArrayBuffer *abuf)
{
uint32_t size_log2, size_elem;
struct list_head *el;
JSTypedArray *ta;
JSObject *p;
uint8_t *data;
int64_t len;
len = abuf->byte_length;
data = abuf->data;
// update lengths of all typed arrays backed by this array buffer
list_for_each(el, &abuf->array_list) {
ta = list_entry(el, JSTypedArray, link);
p = ta->obj;
if (p->class_id == JS_CLASS_DATAVIEW) {
continue;
} else {
p->u.array.count = 0;
p->u.array.u.ptr = NULL;
size_log2 = typed_array_size_log2(p->class_id);
size_elem = 1 << size_log2;
if (ta->track_rab) {
if (len >= (int64_t)ta->offset + size_elem) {
p->u.array.count = (len - ta->offset) >> size_log2;
p->u.array.u.ptr = &data[ta->offset];
}
} else {
if (len >= (int64_t)ta->offset + ta->length) {
p->u.array.count = ta->length >> size_log2;
p->u.array.u.ptr = &data[ta->offset];
}
}
}
}
}
void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj) void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj)
{ {
JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER); JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER);
struct list_head *el;
if (!abuf || abuf->detached) if (!abuf || abuf->detached)
return; return;
@@ -55356,19 +55393,7 @@ void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj)
abuf->data = NULL; abuf->data = NULL;
abuf->byte_length = 0; abuf->byte_length = 0;
abuf->detached = TRUE; abuf->detached = TRUE;
js_array_buffer_update_typed_arrays(abuf);
list_for_each(el, &abuf->array_list) {
JSTypedArray *ta;
JSObject *p;
ta = list_entry(el, JSTypedArray, link);
p = ta->obj;
/* Note: the typed array length and offset fields are not modified */
if (p->class_id != JS_CLASS_DATAVIEW) {
p->u.array.count = 0;
p->u.array.u.ptr = NULL;
}
}
} }
/* get an ArrayBuffer or SharedArrayBuffer */ /* get an ArrayBuffer or SharedArrayBuffer */
@@ -55483,6 +55508,7 @@ static JSValue js_array_buffer_transfer(JSContext *ctx,
abuf->data = NULL; abuf->data = NULL;
abuf->byte_length = 0; abuf->byte_length = 0;
abuf->detached = TRUE; abuf->detached = TRUE;
js_array_buffer_update_typed_arrays(abuf);
return js_array_buffer_constructor3(ctx, JS_UNDEFINED, new_len, pmax_len, return js_array_buffer_constructor3(ctx, JS_UNDEFINED, new_len, pmax_len,
JS_CLASS_ARRAY_BUFFER, JS_CLASS_ARRAY_BUFFER,
bs, free_func, bs, free_func,
@@ -55493,11 +55519,7 @@ static JSValue js_array_buffer_transfer(JSContext *ctx,
static JSValue js_array_buffer_resize(JSContext *ctx, JSValueConst this_val, static JSValue js_array_buffer_resize(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int class_id) int argc, JSValueConst *argv, int class_id)
{ {
uint32_t size_log2, size_elem;
struct list_head *el;
JSArrayBuffer *abuf; JSArrayBuffer *abuf;
JSTypedArray *ta;
JSObject *p;
uint8_t *data; uint8_t *data;
int64_t len; int64_t len;
@@ -55540,29 +55562,7 @@ static JSValue js_array_buffer_resize(JSContext *ctx, JSValueConst this_val,
abuf->byte_length = len; abuf->byte_length = len;
abuf->data = data; abuf->data = data;
} }
data = abuf->data; js_array_buffer_update_typed_arrays(abuf);
// update lengths of all typed arrays backed by this array buffer
list_for_each(el, &abuf->array_list) {
ta = list_entry(el, JSTypedArray, link);
p = ta->obj;
if (p->class_id == JS_CLASS_DATAVIEW)
continue;
p->u.array.count = 0;
p->u.array.u.ptr = NULL;
size_log2 = typed_array_size_log2(p->class_id);
size_elem = 1 << size_log2;
if (ta->track_rab) {
if (len >= (int64_t)ta->offset + size_elem) {
p->u.array.count = (len - ta->offset) >> size_log2;
p->u.array.u.ptr = &data[ta->offset];
}
} else {
if (len >= (int64_t)ta->offset + ta->length) {
p->u.array.count = ta->length >> size_log2;
p->u.array.u.ptr = &data[ta->offset];
}
}
}
return JS_UNDEFINED; return JS_UNDEFINED;
} }

View File

@@ -511,6 +511,14 @@ function test_typed_array()
assert(a.toString(), "1,2,3,4"); assert(a.toString(), "1,2,3,4");
a.set([10, 11], 2); a.set([10, 11], 2);
assert(a.toString(), "1,2,10,11"); assert(a.toString(), "1,2,10,11");
// https://github.com/quickjs-ng/quickjs/issues/1208
buffer = new ArrayBuffer(16);
a = new Uint8Array(buffer);
a.fill(42);
assert(a[0], 42);
buffer.transfer();
assert(a[0], undefined);
} }
/* return [s, line_num, col_num] where line_num and col_num are the /* return [s, line_num, col_num] where line_num and col_num are the