拟态决赛的时间在工作日,听说有一道 JerryScript 的 Pwn 题。作为苦逼打工人,周五抽空尝试做了一下。当时构造出 8 字节的越界读写后,一直尝试利用,但由于 GC 的原因一直没有成功,时间比较短,如果再多些时间应该也是可以利用成功的
不过在调试过程中,了解了该引擎的一些机制,发现相比 v8 还是简单很多的。赛后与其他师傅交流,才发现原来可以通过 Patch 中的漏洞实现任意长度的越界读写。因此尝试复现了一下,诞生了这篇 writeup。
题目给了几个程序运行的链接库,看了下版本是Ubuntu GLIBC 2.39-0ubuntu8.6,由于我本地位wsl2 ubuntu22版本,所以patch了一下
查看jerryscript的版本信息
接着本地编译一个,最后会看到build/bin目录下有一个jerry的可执行文件
类型的定义位于这个文件中jerryscript/jerry-core/ecma/base/ecma-globals.h
ecma_object_t是类型header的开始部分,其中主要的字段有type、gc_next_cp、u1、u2
其中的u1和u2分别代表properties和prototype相关,不是每一个对象都有这两个字段
type_flags_refs中的Type就指的是类型,但是并不像v8那样细分为object arr、double arr……
其中的refs,这个对于利用的稳定性比较重要,如果产生了越界,可以通过修改这个字段不让改对象被gc回收,从而保持布局的稳定性。
然后笔者在实际利用过程中并没有这样,当时没有意识到,回头翻看源码的时候才发现。所以采用了人为构造函数进行ref,增加ref count
下面是一个简单的图示
接着的gc_next_cp是用于gc回收时扫描对象而设立的字段,u1与properties相关,在受限的情况下,可以采用修改和这个字段的方式进行类型混淆,u2和原型链有关,暂时也没想到这个怎么用
笔者尝试过,当时由于稳定性的原因,没有构造出很好用的原语,等待后续研究……
下面是ecma_extended_object_t结构体
简化完毕其实是这样。ecma_object_t object和一个union u
其中的object就是上方的通用类型的header,对于复杂类型会使用到ecma_extended_object_t,其中的union u会根据不同的类型选择不同的字段,以此定义不同对象的属性字段
下面笔者迁移了部分v8 exploit的知识,通过ai写出了一个针对于jerryscript调试的gdb插件,提升了调试的效率
这里以dataview为例子
dataview的定义
arraybuffer的定义
实际内存中是这样的,后方的0x10是对应的byte_offset。0x64eccc323748是arraybuffer的地址
注意下方的0x41414141,这里是对应的inline表示,这个并不利于后续的利用,这个是利用的后话了

如何让arraybuffer分配出一个raw pointer呢?这里只需要提高arraybuffer分配的大小即可,我这里提升到了0x1000

diff的内容
删去了一个对于nan的检查,定位源码可以找到代码的上下文,调用函数是[A] ecma_op_to_integer,从而可以找到上层的调用上下文分别是[B] ecma_op_to_length和[C] ecma_op_to_index函数
接着就是更上层的调用查找,对于ecma_op_to_length来说,更多的是倾向于被字符串和regexp的处理,如果存在漏洞,那么品相也不一定很好,所以我这里继续看了ecma_op_to_index的上层调用

ecma_op_to_index的上层调用如下,这里可以看到很具有代表意义的两个对象,dataview和typearray,如果熟悉v8 exploit的话,这里两个对象的嫌疑最大 ,事实也确实如此,所以接下来继续审计dataview相关的实现
typearray的代码似乎没有很明显的漏洞,因此主要审计了dataview

首先我们得看一下jerryscript中的dataview对象的结构,所有对象的结果为与这个文件下jerryscript/jerry-core/ecma/base/ecma-globals.h
可以看到使用了ecma_extended_object_t的header,这个是对于复杂对象的header,其中集成了ecma_object_t的内容。接着又一个buffer_p指针,这个其实指向了Arraybuffer,接着是对应的byte_offset,用户索引Arraybuffer中的偏移
接着看Arraybuffer的对象结构,结构很简单,其中的buffer_p也就类似于v8中的backingstore
接着可以动态的看一下,测试代码入下
其中地址0x62e46178e5f0中的dword 0x10就是这里设置的byte_offset

同时dataview还支持这样的语法,也就是会有一个view_offset,下发设置了view_offset为0x10,但是索引了0x20的位置,这个是不合法的,会报错
审计代码路径位于jerryscript/jerry-core/ecma/operations/ecma-dataview-object.c
先审计ecma_op_dataview_create这个函数,关于dataview对象的创建,代码如下
这里首先会通过参数列表获取到buffer,这个就是Arraybuffer,接着会检查这个用户传入的Arraybuffer的值是否合法,也就是真实类型是否为Arraybuffer
接着通过参数列表为offset赋值,同时检查是否有问题,可以看到上方[a]处调用了ecma_op_to_index,这个函数涉及到nan的处理,正常遇到nan会将nan清空为0,并返回正常的状态码ECMA_VALUE_EMPTY,但是这里没有清空,所以会正常绕过这个检查,并保留原有的nan的值

接着获取Arraybuffer的长度,并赋值给buffer_byte_length。然后进入if (offset > buffer_byte_length)判断

问题其实就出现在这个地方,这里的比较逻辑是将nan的值从栈上取出来,赋值给xmm0,也就是浮点数寄存器,接着调用comisd进行比较。
需要注意的在 x86/x64 汇编中,comisd 指令在遇到 NaN 时,如果任一操作数是 NaN,它会设置 ZF=1, PF=1, CF=1,接下来的jbe,它的跳转条件是 CF=1 或 ZF=1,所以这里只要涉及到NaN的比较,这里都会被解释成offset <= length,结果就是绕过这个bound check

调试下eflag,没有比较之前是这样
比较之后是这样,成功绕过了这个检查
现在可以得到一个结论,对于这个检查if (offset > buffer_byte_length)
offset = NaN 时,可以直接pass
同样的,这个绕过模式还可以传播为(NaN + arb_val > buffer_byte_length)→false,所以我们可以在NaN后面加上任意偏移,这个也就是上方的[b]处,if (offset + byte_length_to_index > buffer_byte_length)
进入[b]处也很简单,参数是三个就行,也就用到了上方的语法
这里,我将view_byte_length设置为0xfffffff,buffer_byte_length仅为0x100,但是由于offset是nan,所以此时的eflag如下,也就绕过了bound check

最终的NaN被类型转化为uint32_t变成0,但是view_byte_length成功赋值为0xffffffff。至此我们已经成功构造了一个存在越界的dataview对象,我们现在需要接着分析对于dataview的get和set操作,看一下是否可以将这个漏洞扩大,变成一个可以越界读写的原语

有了阅读ecma_op_dataview_create的经验,我们其实只需要找这个函数中对于边界检查的部分,通过最后的对象属性赋值部分来验证猜想,所以这个函数的代码被精简如下
构造的调试代码如下,这里是构造了一个dv的越界越界读操作
首先分析上面的源码
注释1-3部分是对于dataview header和arraybuffer header的检查,如果说单纯修改指针,那么这个检查过不去是没用的
接着直接来到上方[a]部分,也就是这里的边界检查if (get_index + element_size > (ecma_number_t) view_size),这个逻辑很正常,就是检查用户传入的index+取出的elment size是否超过了views_size。
但是需要注意的是这里的view_size已经被我们修改成了0xffffffff,所以这里相当于直接绕过了这个检查
下方的getindex是0x200,已经超过了arraybuffer的长度,但是由于view_size被修改,所以越界了,后续就是存值取值的操作

由于笔者也是第一次接触jerryscript的利用,所以最后的脚本经过大量调试得到,所以笔者这里就解释下写利用的思路
我们之前已经得到了一个越界的dv,现在需要思考如何可控。
上面提到了可以通过申请0x1000这样length的arraybuffer,这样就可以让arraybuffer不inline表示,从而分配出一个raw pointer。因此可以通过一个越界的dv去读取内存后方arraybuffer中的buffer_p字段,那么现在至少存在了一个jerry heap的地址。

接着的问题是如何去定位这个arraybuffer,首先我需要保证我的越界dv地址在受害arraybuffer的前方,所以需要调试一下,这里发现是没有问题的

最简单的定位思路就是写一个特征值,但问题是这样只能扫描到特征值的位置,我们通过特征值的位置无法定位到arraybuffer指向的位置。像下面红框中一样,只能扫描到这个值,但是无法反推。

解决这个问题就需要思考arraybuffer的特征,arraybuffer的特征由前面的0x10个字节的header决定,下发的四个字段中有type、gc_next、prototype……
因此我选取了Type、ProtoType、ByteLength,这三个字段在我堆喷出来的对象中是一致的,所以我可以通过header的字段来确定受害arraybuffer的位置,那么相邻的就是jerry_global_heap段上的值
可以得到这样的一个leak,通过减去0x11a30可以得到一个段开头的地址,但是这个是随机的,根据你写的脚本和环境决定

所以为了稳定性我进行了如下的计算,最后的情况是在多0x9000和少0x9000的情况下摆动
可以看到jerry_global_heap起始地址是这个段的开头+0x280

同时这个jerry_global_heap开头的一段内存上是存在函数指针的值,所以我们接下来需要思考如何去利用这个jerry_global_heap来泄漏出code_base、libc等值

通过上方的思路,确实定位到了受害arraybuffer的位置,但是由于这里的arraybuffer是堆喷出来的(如下所示),所以我还需要确定这个arrybuffer的具体位置
确定受害arraybuffer的思路是也很清晰,我这里已经可以越界读定位到受害arraybuffer的header,所以可以通过越界写修改arraybuffer的byteLength字段,然后遍历所有的arraybuffer来检查哪一个对象的byteLength被修改了,至此我们已经可以准确定位arraybuffer了
定位到arraybuffer,下面的任意读写就是修改buffer_p字段,然后调用dataview的get/set方法即可
这里是为了提高任意读写的稳定性,如何笔者发现完成一次任意读写这里就会触发gc移动,如果此时的buffer_p是一个gc无法回收的地址,那么就会程序崩溃,所以解决思路也很简单,任意读写完毕之后,把原本的buffer_p再修改回去。
同时,为了防止原本布置的victim对象被gc回收,可以通过如下方式增加ref count
也可以通过上面笔者提到的思路,直接利用越界去修改ref count的值,这样也可以
存在任意读写之后,可以通过读jerry_global_heap段开头的一些handler函数的值,确定code base的值,从而定位到got,然后泄漏位于libc中函数的值

这里笔者由于很久没有接触house of打法,已经不知道2.39应该怎么做了,所以这里采用了通过environ泄漏栈地址的方式,然后劫持main函数的返回地址,实现rop
本地测试时堆地址会在这两个之前变化,一个没打通试另外一个就可以了

成功则会有如下显示

➜ jerry ./jerry --version
Version: 3.0.0 (b7069350)
➜ jerry ./jerry --version
Version: 3.0.0 (b7069350)
git clone https://github.com/jerryscript-project/jerryscript.git
cd jerryscript
git checkout b7069350
patch -p1 < ../patch
python tools/build.py --debug --lto=off
git clone https://github.com/jerryscript-project/jerryscript.git
cd jerryscript
git checkout b7069350
patch -p1 < ../patch
python tools/build.py --debug --lto=off
typedef struct
{
ecma_object_descriptor_t type_flags_refs;
jmem_cpointer_t gc_next_cp;
union
{
jmem_cpointer_t property_list_cp;
jmem_cpointer_t bound_object_cp;
jmem_cpointer_t home_object_cp;
} u1;
union
{
jmem_cpointer_t prototype_cp;
jmem_cpointer_t outer_reference_cp;
} u2;
} ecma_object_t;
typedef struct
{
ecma_object_descriptor_t type_flags_refs;
jmem_cpointer_t gc_next_cp;
union
{
jmem_cpointer_t property_list_cp;
jmem_cpointer_t bound_object_cp;
jmem_cpointer_t home_object_cp;
} u1;
union
{
jmem_cpointer_t prototype_cp;
jmem_cpointer_t outer_reference_cp;
} u2;
} ecma_object_t;
| 31 ......................... 6 | 5 ...... 4 | 3 ........ 0 |
| Reference Count | Flags | Type |
| (26 bits) | (2 bits) | (4 bits) |
| 31 ......................... 6 | 5 ...... 4 | 3 ........ 0 |
| Reference Count | Flags | Type |
| (26 bits) | (2 bits) | (4 bits) |
typedef struct
{
ecma_object_t object;
union
{
ecma_built_in_props_t built_in;
struct
{
uint8_t type;
union
{
uint8_t arguments_flags;
uint8_t error_type;
#if JERRY_BUILTIN_DATE
uint8_t date_flags;
#endif /* JERRY_BUILTIN_DATE */
#if JERRY_MODULE_SYSTEM
uint8_t module_state;
#endif /* JERRY_MODULE_SYSTEM */
uint8_t iterator_kind;
uint8_t regexp_string_iterator_flags;
uint8_t promise_flags;
#if JERRY_BUILTIN_CONTAINER
uint8_t container_flags;
#endif /* JERRY_BUILTIN_CONTAINER */
#if JERRY_BUILTIN_TYPEDARRAY
uint8_t array_buffer_flags;
uint8_t typedarray_type;
#endif /* JERRY_BUILTIN_TYPEDARRAY */
} u1;
union
{
uint16_t formal_params_number;
#if JERRY_MODULE_SYSTEM
uint16_t module_flags;
#endif /* JERRY_MODULE_SYSTEM */
uint16_t iterator_index;
uint16_t executable_obj_flags;
#if JERRY_BUILTIN_CONTAINER
uint16_t container_id;
#endif /* JERRY_BUILTIN_CONTAINER */
#if JERRY_BUILTIN_TYPEDARRAY
uint16_t typedarray_flags;
#endif /* JERRY_BUILTIN_TYPEDARRAY */
} u2;
union
{
ecma_value_t value;
ecma_value_t target;
#if JERRY_BUILTIN_TYPEDARRAY
ecma_value_t arraybuffer;
#endif /* JERRY_BUILTIN_TYPEDARRAY */
ecma_value_t head;
ecma_value_t iterated_value;
ecma_value_t promise;
ecma_value_t sync_iterator;
ecma_value_t spread_value;
int32_t tza;
uint32_t length;
uint32_t arguments_number;
#if JERRY_MODULE_SYSTEM
uint32_t dfs_ancestor_index;
#endif /* JERRY_MODULE_SYSTEM */
} u3;
} cls;
struct
{
jmem_cpointer_tag_t scope_cp;
ecma_value_t bytecode_cp;
} function;
struct
{
uint32_t length;
uint32_t length_prop_and_hole_count;
} array;
struct
{
jmem_cpointer_tag_t target_function;
ecma_value_t args_len_or_this;
} bound_function;
struct
{
ecma_value_t script_value;
uint8_t flags;
} constructor_function;
} u;
} ecma_extended_object_t;
typedef struct
{
ecma_object_t object;
union
{
ecma_built_in_props_t built_in;
struct
{
uint8_t type;
union
{
uint8_t arguments_flags;
uint8_t error_type;
#if JERRY_BUILTIN_DATE
uint8_t date_flags;
#endif /* JERRY_BUILTIN_DATE */
#if JERRY_MODULE_SYSTEM
uint8_t module_state;
#endif /* JERRY_MODULE_SYSTEM */
uint8_t iterator_kind;
uint8_t regexp_string_iterator_flags;
uint8_t promise_flags;
#if JERRY_BUILTIN_CONTAINER
uint8_t container_flags;
#endif /* JERRY_BUILTIN_CONTAINER */
#if JERRY_BUILTIN_TYPEDARRAY
uint8_t array_buffer_flags;
uint8_t typedarray_type;
#endif /* JERRY_BUILTIN_TYPEDARRAY */
} u1;
union
{
uint16_t formal_params_number;
#if JERRY_MODULE_SYSTEM
uint16_t module_flags;
#endif /* JERRY_MODULE_SYSTEM */
uint16_t iterator_index;
uint16_t executable_obj_flags;
#if JERRY_BUILTIN_CONTAINER
uint16_t container_id;
#endif /* JERRY_BUILTIN_CONTAINER */
#if JERRY_BUILTIN_TYPEDARRAY
uint16_t typedarray_flags;
#endif /* JERRY_BUILTIN_TYPEDARRAY */
} u2;
union
{
ecma_value_t value;
ecma_value_t target;
#if JERRY_BUILTIN_TYPEDARRAY
ecma_value_t arraybuffer;
#endif /* JERRY_BUILTIN_TYPEDARRAY */
ecma_value_t head;
ecma_value_t iterated_value;
ecma_value_t promise;
ecma_value_t sync_iterator;
ecma_value_t spread_value;
int32_t tza;
uint32_t length;
uint32_t arguments_number;
#if JERRY_MODULE_SYSTEM
uint32_t dfs_ancestor_index;
#endif /* JERRY_MODULE_SYSTEM */
} u3;
} cls;
struct
{
jmem_cpointer_tag_t scope_cp;
ecma_value_t bytecode_cp;
} function;
struct
{
uint32_t length;
uint32_t length_prop_and_hole_count;
} array;
struct
{
jmem_cpointer_tag_t target_function;
ecma_value_t args_len_or_this;
} bound_function;
struct
{
ecma_value_t script_value;
uint8_t flags;
} constructor_function;
} u;
} ecma_extended_object_t;
typedef struct
{
ecma_object_t object;
union
{
struct
{
uint8_t type;
union {
uint8_t array_buffer_flags;
uint8_t typedarray_type;
} u1;
union {
uint16_t typedarray_flags;
} u2;
union {
uint32_t length;
ecma_value_t arraybuffer;
ecma_value_t value;
} u3;
} cls;
struct
{
uint32_t length;
uint32_t length_prop_and_hole_count;
} array;
struct
{
jmem_cpointer_tag_t scope_cp;
ecma_value_t bytecode_cp;
} function;
struct
{
jmem_cpointer_tag_t target_function;
ecma_value_t args_len_or_this;
} bound_function;
} u;
} ecma_extended_object_t;
typedef struct
{
ecma_object_t object;
union
{
struct
{
uint8_t type;
union {
uint8_t array_buffer_flags;
uint8_t typedarray_type;
} u1;
union {
uint16_t typedarray_flags;
} u2;
union {
uint32_t length;
ecma_value_t arraybuffer;
ecma_value_t value;
} u3;
} cls;
struct
{
uint32_t length;
uint32_t length_prop_and_hole_count;
} array;
struct
{
jmem_cpointer_tag_t scope_cp;
ecma_value_t bytecode_cp;
} function;
struct
{
jmem_cpointer_tag_t target_function;
ecma_value_t args_len_or_this;
} bound_function;
} u;
} ecma_extended_object_t;
let ab = new ArrayBuffer(0x100);
let dv = new DataView(ab,0x10,0x20);
dv.setUint32(0x00,0x41414141,true);
let ab = new ArrayBuffer(0x100);
let dv = new DataView(ab,0x10,0x20);
dv.setUint32(0x00,0x41414141,true);
typedef struct
{
ecma_extended_object_t header;
ecma_object_t *buffer_p;
uint32_t byte_offset;
} ecma_dataview_object_t;
typedef struct
{
ecma_extended_object_t header;
ecma_object_t *buffer_p;
uint32_t byte_offset;
} ecma_dataview_object_t;
typedef struct
{
ecma_extended_object_t extended_object;
void *buffer_p;
void *arraybuffer_user_p;
} ecma_arraybuffer_pointer_t;
typedef struct
{
ecma_extended_object_t extended_object;
void *buffer_p;
void *arraybuffer_user_p;
} ecma_arraybuffer_pointer_t;
diff --git a/jerry-core/ecma/operations/ecma-conversion.c b/jerry-core/ecma/operations/ecma-conversion.c
index cf0c9fde..5c1b7aa2 100644
--- a/jerry-core/ecma/operations/ecma-conversion.c
+++ b/jerry-core/ecma/operations/ecma-conversion.c
@@ -905,7 +905,6 @@ ecma_op_to_integer (ecma_value_t value, /**< ecma value */
/* 3 */
if (ecma_number_is_nan (number))
{
- *number_p = ECMA_NUMBER_ZERO;
return ECMA_VALUE_EMPTY;
}
diff --git a/jerry-core/ecma/operations/ecma-conversion.c b/jerry-core/ecma/operations/ecma-conversion.c
index cf0c9fde..5c1b7aa2 100644
--- a/jerry-core/ecma/operations/ecma-conversion.c
+++ b/jerry-core/ecma/operations/ecma-conversion.c
@@ -905,7 +905,6 @@ ecma_op_to_integer (ecma_value_t value, /**< ecma value */
/* 3 */
if (ecma_number_is_nan (number))
{
- *number_p = ECMA_NUMBER_ZERO;
return ECMA_VALUE_EMPTY;
}
ecma_value_t
ecma_op_to_integer (ecma_value_t value,
ecma_number_t *number_p)
{
......
ecma_number_t number = *number_p;
if (ecma_number_is_nan (number))
{
return ECMA_VALUE_EMPTY;
}
......
ecma_value_t
ecma_op_to_length (ecma_value_t value,
ecma_length_t *length)
{
if (ECMA_IS_VALUE_ERROR (value))
{
return value;
}
ecma_number_t num;
ecma_value_t length_num = ecma_op_to_integer (value, &num);
......
ecma_value_t
ecma_op_to_index (ecma_value_t value,
ecma_number_t *index)
{
if (ecma_is_value_undefined (value))
{
*index = 0;
return ECMA_VALUE_EMPTY;
}
ecma_number_t integer_index;
ecma_value_t index_value = ecma_op_to_integer (value, &integer_index);
ecma_value_t
ecma_op_to_integer (ecma_value_t value,
ecma_number_t *number_p)
{
......
ecma_number_t number = *number_p;
if (ecma_number_is_nan (number))
{
return ECMA_VALUE_EMPTY;
}
......
ecma_value_t
ecma_op_to_length (ecma_value_t value,
ecma_length_t *length)
{
if (ECMA_IS_VALUE_ERROR (value))
{
return value;
}
ecma_number_t num;
ecma_value_t length_num = ecma_op_to_integer (value, &num);
......
ecma_value_t
ecma_op_to_index (ecma_value_t value,
ecma_number_t *index)
{
if (ecma_is_value_undefined (value))
{
*index = 0;
return ECMA_VALUE_EMPTY;
}
ecma_number_t integer_index;
ecma_value_t index_value = ecma_op_to_integer (value, &integer_index);
#if JERRY_BUILTIN_DATAVIEW
typedef struct
{
ecma_extended_object_t header;
ecma_object_t *buffer_p;
uint32_t byte_offset;
} ecma_dataview_object_t;
#endif /* JERRY_BUILTIN_DATAVIEW */
#if JERRY_BUILTIN_DATAVIEW
typedef struct
{
ecma_extended_object_t header;
ecma_object_t *buffer_p;
uint32_t byte_offset;
} ecma_dataview_object_t;
#endif /* JERRY_BUILTIN_DATAVIEW */
typedef struct
{
ecma_extended_object_t extended_object;
void *buffer_p;
void *arraybuffer_user_p;
} ecma_arraybuffer_pointer_t;
typedef struct
{
ecma_extended_object_t extended_object;
void *buffer_p;
void *arraybuffer_user_p;
} ecma_arraybuffer_pointer_t;
let ab = new ArrayBuffer(0x100);
let dv = new DataView(ab,0x10);
dv.setUint32(0,0x11111111,true);
let ab = new ArrayBuffer(0x100);
let dv = new DataView(ab,0x10);
dv.setUint32(0,0x11111111,true);
let ab = new ArrayBuffer(0x100);
let dv = new DataView(ab,0x10,0x10);
dv.setUint32(0x20,0x11111111,true);
let ab = new ArrayBuffer(0x100);
let dv = new DataView(ab,0x10,0x10);
dv.setUint32(0x20,0x11111111,true);
ecma_value_t
ecma_op_dataview_create (const ecma_value_t *arguments_list_p,
uint32_t arguments_list_len)
{
JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL);
JERRY_ASSERT (JERRY_CONTEXT (current_new_target_p));
ecma_value_t buffer = arguments_list_len > 0 ? arguments_list_p[0] : ECMA_VALUE_UNDEFINED;
if (!ecma_is_value_object (buffer))
{
return ecma_raise_type_error (ECMA_ERR_ARGUMENT_BUFFER_NOT_OBJECT);
}
ecma_object_t *buffer_p = ecma_get_object_from_value (buffer);
if (!(ecma_object_class_is (buffer_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER)
|| ecma_object_is_shared_arraybuffer (buffer_p)))
{
return ecma_raise_type_error (ECMA_ERR_ARGUMENT_BUFFER_NOT_ARRAY_OR_SHARED_BUFFER);
}
ecma_number_t offset = 0;
if (arguments_list_len > 1)
{
ecma_value_t offset_value = ecma_op_to_index (arguments_list_p[1], &offset);
if (ECMA_IS_VALUE_ERROR (offset_value))
{
return offset_value;
}
}
if (ecma_arraybuffer_is_detached (buffer_p))
{
return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED);
}
ecma_number_t buffer_byte_length = ecma_arraybuffer_get_length (buffer_p);
if (offset > buffer_byte_length)
{
return ecma_raise_range_error (ECMA_ERR_START_OFFSET_IS_OUTSIDE_THE_BOUNDS_OF_THE_BUFFER);
}
uint32_t view_byte_length;
if (arguments_list_len > 2 && !ecma_is_value_undefined (arguments_list_p[2]))
{
ecma_number_t byte_length_to_index;
ecma_value_t byte_length_value = ecma_op_to_index (arguments_list_p[2], &byte_length_to_index);
if (ECMA_IS_VALUE_ERROR (byte_length_value))
{
return byte_length_value;
}
if (offset + byte_length_to_index > buffer_byte_length)
{
return ecma_raise_range_error (ECMA_ERR_START_OFFSET_IS_OUTSIDE_THE_BOUNDS_OF_THE_BUFFER);
}
JERRY_ASSERT (byte_length_to_index <= UINT32_MAX);
view_byte_length = (uint32_t) byte_length_to_index;
}
else
{
view_byte_length = (uint32_t) (buffer_byte_length - offset);
}
ecma_object_t *prototype_obj_p =
ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_DATAVIEW_PROTOTYPE);
if (JERRY_UNLIKELY (prototype_obj_p == NULL))
{
return ECMA_VALUE_ERROR;
}
if (ecma_arraybuffer_is_detached (buffer_p))
{
ecma_deref_object (prototype_obj_p);
return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED);
}
ecma_object_t *object_p =
ecma_create_object (prototype_obj_p, sizeof (ecma_dataview_object_t), ECMA_OBJECT_TYPE_CLASS);
ecma_deref_object (prototype_obj_p);
ecma_dataview_object_t *dataview_obj_p = (ecma_dataview_object_t *) object_p;
dataview_obj_p->header.u.cls.type = ECMA_OBJECT_CLASS_DATAVIEW;
dataview_obj_p->header.u.cls.u3.length = view_byte_length;
dataview_obj_p->buffer_p = buffer_p;
dataview_obj_p->byte_offset = (uint32_t) offset;
return ecma_make_object_value (object_p);
}
ecma_value_t
ecma_op_dataview_create (const ecma_value_t *arguments_list_p,
uint32_t arguments_list_len)
{
JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL);
JERRY_ASSERT (JERRY_CONTEXT (current_new_target_p));
ecma_value_t buffer = arguments_list_len > 0 ? arguments_list_p[0] : ECMA_VALUE_UNDEFINED;
if (!ecma_is_value_object (buffer))
{
return ecma_raise_type_error (ECMA_ERR_ARGUMENT_BUFFER_NOT_OBJECT);
}
ecma_object_t *buffer_p = ecma_get_object_from_value (buffer);
if (!(ecma_object_class_is (buffer_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER)
|| ecma_object_is_shared_arraybuffer (buffer_p)))
{
return ecma_raise_type_error (ECMA_ERR_ARGUMENT_BUFFER_NOT_ARRAY_OR_SHARED_BUFFER);
}
ecma_number_t offset = 0;
if (arguments_list_len > 1)
{
ecma_value_t offset_value = ecma_op_to_index (arguments_list_p[1], &offset);
if (ECMA_IS_VALUE_ERROR (offset_value))
{
return offset_value;
}
}
if (ecma_arraybuffer_is_detached (buffer_p))
{
return ecma_raise_type_error (ECMA_ERR_ARRAYBUFFER_IS_DETACHED);
}
ecma_number_t buffer_byte_length = ecma_arraybuffer_get_length (buffer_p);
if (offset > buffer_byte_length)
{
return ecma_raise_range_error (ECMA_ERR_START_OFFSET_IS_OUTSIDE_THE_BOUNDS_OF_THE_BUFFER);
}
uint32_t view_byte_length;
if (arguments_list_len > 2 && !ecma_is_value_undefined (arguments_list_p[2]))
{
ecma_number_t byte_length_to_index;
ecma_value_t byte_length_value = ecma_op_to_index (arguments_list_p[2], &byte_length_to_index);
if (ECMA_IS_VALUE_ERROR (byte_length_value))
{
return byte_length_value;
}
if (offset + byte_length_to_index > buffer_byte_length)
{
return ecma_raise_range_error (ECMA_ERR_START_OFFSET_IS_OUTSIDE_THE_BOUNDS_OF_THE_BUFFER);
}
JERRY_ASSERT (byte_length_to_index <= UINT32_MAX);
view_byte_length = (uint32_t) byte_length_to_index;
}
else
{
view_byte_length = (uint32_t) (buffer_byte_length - offset);
}
ecma_object_t *prototype_obj_p =
ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target_p), ECMA_BUILTIN_ID_DATAVIEW_PROTOTYPE);
if (JERRY_UNLIKELY (prototype_obj_p == NULL))
{
return ECMA_VALUE_ERROR;
}
if (ecma_arraybuffer_is_detached (buffer_p))
{
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2025-12-3 21:55
被flyyyy编辑
,原因: