fixed TypedArray sort semantics by copying the array before calling the comparison function. Fixed buffer overflow when the array is resized (#477)

This commit is contained in:
Fabrice Bellard 2026-03-21 11:33:13 +01:00
parent 4c722cea4e
commit 0989d4cb4a
3 changed files with 49 additions and 55 deletions

2
TODO

@ -63,4 +63,4 @@ Test262o: 0/11262 errors, 463 excluded
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
Test262:
Result: 66/83341 errors, 2567 excluded, 5767 skipped
Result: 64/83341 errors, 2567 excluded, 5767 skipped

100
quickjs.c

@ -57676,7 +57676,7 @@ static JSValue js_TA_get_float64(JSContext *ctx, const void *a) {
struct TA_sort_context {
JSContext *ctx;
int exception; /* 1 = exception, 2 = detached typed array */
JSValueConst arr;
uint8_t *array;
JSValueConst cmp;
JSValue (*getfun)(JSContext *ctx, const void *a);
int elt_size;
@ -57689,7 +57689,6 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {
JSValueConst argv[2];
JSValue res;
int cmp;
JSObject *p;
cmp = 0;
if (!psc->exception) {
@ -57697,15 +57696,9 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {
error */
a_idx = *(uint32_t *)a;
b_idx = *(uint32_t *)b;
p = JS_VALUE_GET_PTR(psc->arr);
if (a_idx >= p->u.array.count || b_idx >= p->u.array.count) {
/* OOB case */
psc->exception = 2;
return 0;
}
argv[0] = psc->getfun(ctx, p->u.array.u.uint8_ptr +
argv[0] = psc->getfun(ctx, psc->array +
a_idx * (size_t)psc->elt_size);
argv[1] = psc->getfun(ctx, p->u.array.u.uint8_ptr +
argv[1] = psc->getfun(ctx, psc->array +
b_idx * (size_t)(psc->elt_size));
res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv);
if (JS_IsException(res)) {
@ -57746,7 +57739,6 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
tsc.ctx = ctx;
tsc.exception = 0;
tsc.arr = this_val;
tsc.cmp = argv[0];
if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp))
@ -57809,65 +57801,69 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
elt_size = 1 << typed_array_size_log2(p->class_id);
if (!JS_IsUndefined(tsc.cmp)) {
uint32_t *array_idx;
void *array_tmp;
void *array;
size_t i, j;
/* XXX: a stable sort would use less memory */
array_idx = js_malloc(ctx, len * sizeof(array_idx[0]));
if (!array_idx)
/* the array must be copied because the comparison
function may modify it */
array = js_malloc(ctx, len * elt_size);
if (!array)
return JS_EXCEPTION;
memcpy(array, p->u.array.u.ptr, len * elt_size);
/* array_idx is needed to have a stable sort */
array_idx = js_malloc(ctx, len * sizeof(array_idx[0]));
if (!array_idx) {
js_free(ctx, array);
return JS_EXCEPTION;
}
for(i = 0; i < len; i++)
array_idx[i] = i;
tsc.elt_size = elt_size;
tsc.array = array;
rqsort(array_idx, len, sizeof(array_idx[0]),
js_TA_cmp_generic, &tsc);
if (tsc.exception) {
if (tsc.exception == 1)
goto fail;
if (tsc.exception == 1) {
js_free(ctx, array_idx);
js_free(ctx, array);
return JS_EXCEPTION;
}
/* detached typed array during the sort: no error */
} else {
void *array_ptr = p->u.array.u.ptr;
len = min_int(len, p->u.array.count);
if (len != 0) {
array_tmp = js_malloc(ctx, len * elt_size);
if (!array_tmp) {
fail:
js_free(ctx, array_idx);
return JS_EXCEPTION;
switch(elt_size) {
case 1:
for(i = 0; i < len; i++) {
j = array_idx[i];
((uint8_t *)array_ptr)[i] = ((uint8_t *)array)[j];
}
memcpy(array_tmp, array_ptr, len * elt_size);
switch(elt_size) {
case 1:
for(i = 0; i < len; i++) {
j = array_idx[i];
((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j];
}
break;
case 2:
for(i = 0; i < len; i++) {
j = array_idx[i];
((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j];
}
break;
case 4:
for(i = 0; i < len; i++) {
j = array_idx[i];
((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j];
}
break;
case 8:
for(i = 0; i < len; i++) {
j = array_idx[i];
((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j];
}
break;
default:
abort();
break;
case 2:
for(i = 0; i < len; i++) {
j = array_idx[i];
((uint16_t *)array_ptr)[i] = ((uint16_t *)array)[j];
}
js_free(ctx, array_tmp);
break;
case 4:
for(i = 0; i < len; i++) {
j = array_idx[i];
((uint32_t *)array_ptr)[i] = ((uint32_t *)array)[j];
}
break;
case 8:
for(i = 0; i < len; i++) {
j = array_idx[i];
((uint64_t *)array_ptr)[i] = ((uint64_t *)array)[j];
}
break;
default:
abort();
}
}
js_free(ctx, array_idx);
js_free(ctx, array);
} else {
rqsort(p->u.array.u.ptr, len, elt_size, cmpfun, &tsc);
if (tsc.exception)

@ -37,8 +37,6 @@ test262/test/staging/sm/TypedArray/constructor-buffer-sequence.js:29: Test262Err
test262/test/staging/sm/TypedArray/constructor-buffer-sequence.js:29: strict mode: Test262Error: Expected a ExpectedError but got a Error
test262/test/staging/sm/TypedArray/prototype-constructor-identity.js:17: Test262Error: Expected SameValue(«2», «6») to be true
test262/test/staging/sm/TypedArray/prototype-constructor-identity.js:17: strict mode: Test262Error: Expected SameValue(«2», «6») to be true
test262/test/staging/sm/TypedArray/sort_modifications.js:9: Test262Error: Int8Array at index 0 for size 4 Expected SameValue(«0», «1») to be true
test262/test/staging/sm/TypedArray/sort_modifications.js:9: strict mode: Test262Error: Int8Array at index 0 for size 4 Expected SameValue(«0», «1») to be true
test262/test/staging/sm/async-functions/async-contains-unicode-escape.js:11: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
test262/test/staging/sm/async-functions/async-contains-unicode-escape.js:11: strict mode: Test262Error: Expected a SyntaxError to be thrown but no exception was thrown at all
test262/test/staging/sm/async-functions/await-in-arrow-parameters.js:10: Test262Error: AsyncFunction:(a = (b = await/r/g) => {}) => {} Expected a SyntaxError to be thrown but no exception was thrown at all