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));
}
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)
{
JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER);
struct list_head *el;
if (!abuf || abuf->detached)
return;
@@ -55356,19 +55393,7 @@ void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj)
abuf->data = NULL;
abuf->byte_length = 0;
abuf->detached = TRUE;
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;
}
}
js_array_buffer_update_typed_arrays(abuf);
}
/* get an ArrayBuffer or SharedArrayBuffer */
@@ -55483,6 +55508,7 @@ static JSValue js_array_buffer_transfer(JSContext *ctx,
abuf->data = NULL;
abuf->byte_length = 0;
abuf->detached = TRUE;
js_array_buffer_update_typed_arrays(abuf);
return js_array_buffer_constructor3(ctx, JS_UNDEFINED, new_len, pmax_len,
JS_CLASS_ARRAY_BUFFER,
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,
int argc, JSValueConst *argv, int class_id)
{
uint32_t size_log2, size_elem;
struct list_head *el;
JSArrayBuffer *abuf;
JSTypedArray *ta;
JSObject *p;
uint8_t *data;
int64_t len;
@@ -55540,29 +55562,7 @@ static JSValue js_array_buffer_resize(JSContext *ctx, JSValueConst this_val,
abuf->byte_length = len;
abuf->data = data;
}
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;
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];
}
}
}
js_array_buffer_update_typed_arrays(abuf);
return JS_UNDEFINED;
}