Description
There's an exploitable heap corruption bug, where the garbage collector runs off the end of a bound function object and enters whatever else is after that. Owing to a sanity check on the arguments, this is super exploitable although I don't have an exploit for it.
On Ubuntu 16.04.2,
$ python tools/build.py --compile-flag=-m32 --clean --jerry-libc=OFF --system-allocator ON --compile-flag=-ggdb3 --debug --link-lib mcheck
$ cat x.js
Function.prototype.bind({0:0},30000000000);
$ cat mcheck.gdb
break main
commands
call mcheck(0)
c
end
run
$ MALLOC_CHECK_=1 gdb -q -x mcheck.gdb --args ./build/bin/jerry x.js
Reading symbols from ./build/bin/jerry...done.
Breakpoint 1 at 0x80ac9a8: file /afl/jerryscript/jerry-main/main-unix.c, line 410.
Breakpoint 1, main (argc=0x2, argv=0xffffd654) at /afl/jerryscript/jerry-main/main-unix.c:410
410 {
$1 = 0x0
Program received signal SIGSEGV, Segmentation fault.
0x080a8d7f in ecma_gc_set_object_visited.lto_priv.626 (object_p=0x93939390, is_visited=0x1) at /afl/jerryscript/jerry-core/ecma/base/ecma-gc.c:115
115 object_p->type_flags_refs = (uint16_t) (object_p->type_flags_refs & ~ECMA_OBJECT_FLAG_GC_VISITED);
(gdb) where
#0 0x080a8d7f in ecma_gc_set_object_visited.lto_priv.626 (object_p=0x93939390, is_visited=0x1) at /afl/jerryscript/jerry-core/ecma/base/ecma-gc.c:115
#1 0x080a2731 in ecma_gc_mark (object_p=0x837c150) at /afl/jerryscript/jerry-core/ecma/base/ecma-gc.c:348
#2 0x080a3054 in ecma_gc_run (severity=JMEM_FREE_UNUSED_MEMORY_SEVERITY_LOW) at /afl/jerryscript/jerry-core/ecma/base/ecma-gc.c:732
#3 0x080a3220 in ecma_free_unused_memory (severity=JMEM_FREE_UNUSED_MEMORY_SEVERITY_LOW) at /afl/jerryscript/jerry-core/ecma/base/ecma-gc.c:828
#4 0x0808be2e in jmem_run_free_unused_memory_callbacks (severity=JMEM_FREE_UNUSED_MEMORY_SEVERITY_LOW) at /afl/jerryscript/jerry-core/jmem/jmem-allocator.c:148
#5 0x0808c0b2 in jmem_heap_gc_and_alloc_block (size=0x8, ret_null_on_error=0x0) at /afl/jerryscript/jerry-core/jmem/jmem-heap.c:359
#6 0x0808c175 in jmem_heap_alloc_block (size=0x8) at /afl/jerryscript/jerry-core/jmem/jmem-heap.c:408
#7 0x0808bec9 in jmem_pools_alloc (size=0x8) at /afl/jerryscript/jerry-core/jmem/jmem-poolman.c:102
#8 0x0809bfb6 in ecma_alloc_number () at /afl/jerryscript/jerry-core/ecma/base/ecma-alloc.c:83
#9 0x0809b2fd in ecma_create_float_number (ecma_number=300000000) at /afl/jerryscript/jerry-core/ecma/base/ecma-helpers-value.c:384
#10 0x0809b961 in ecma_copy_value (value=0x837c0f1) at /afl/jerryscript/jerry-core/ecma/base/ecma-helpers-value.c:639
#11 0x0809ba79 in ecma_copy_value_if_not_object (value=0x837c0f1) at /afl/jerryscript/jerry-core/ecma/base/ecma-helpers-value.c:683
#12 0x08074bd2 in ecma_builtin_function_prototype_object_bind (this_arg=0x837bb93, arguments_list_p=0xffffd38c, arguments_number=0x2)
at /afl/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.c:273
#13 0x08074513 in ecma_builtin_function_prototype_dispatch_routine (builtin_routine_id=0x25, this_arg_value=0x837bb93, arguments_list=0xffffd38c, arguments_number=0x2)
at /afl/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.inc.h:43
#14 0x080996c3 in ecma_builtin_dispatch_routine (builtin_object_id=ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, builtin_routine_id=0x25, this_arg_value=0x837bb93, arguments_list=0xffffd38c,
arguments_number=0x2) at /afl/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h:108
#15 0x08099a87 in ecma_builtin_dispatch_call (obj_p=0x837beb0, this_arg_value=0x837bb93, arguments_list_p=0xffffd38c, arguments_list_len=0x2)
at /afl/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtins.c:844
#16 0x08091f9e in ecma_op_function_call (func_obj_p=0x837beb0, this_arg_value=0x837bb93, arguments_list_p=0xffffd38c, arguments_list_len=0x2)
at /afl/jerryscript/jerry-core/ecma/operations/ecma-function-object.c:458
#17 0x08085ce4 in opfunc_call.lto_priv.408 (frame_ctx_p=0xffffd3b4) at /afl/jerryscript/jerry-core/vm/vm.c:411
#18 0x0807db36 in vm_execute (frame_ctx_p=0xffffd3b4, arg_p=0x0, arg_list_len=0x0) at /afl/jerryscript/jerry-core/vm/vm.c:2746
#19 0x0807dcfe in vm_run (bytecode_header_p=0x837bfe8, this_binding_value=0x837bafb, lex_env_p=0x837bb30, is_eval_code=0x0, arg_list_p=0x0, arg_list_len=0x0)
at /afl/jerryscript/jerry-core/vm/vm.c:2826
#20 0x08085939 in vm_run_global (bytecode_p=0x837bfe8) at /afl/jerryscript/jerry-core/vm/vm.c:231
#21 0x080ae327 in jerry_run (func_val=0x837be0b) at /afl/jerryscript/jerry-core/api/jerry.c:425
#22 0x080ad1ce in main (argc=0x2, argv=0xffffd654) at /afl/jerryscript/jerry-main/main-unix.c:691
(gdb)
The garbage collector is expecting a bound function to have an argument list, and it doesn't. Might have been an errant copy-paste between the compilation vs. the garbage collector.
More details from a similar debugging session:
Program received signal SIGSEGV, Segmentation fault.
0x080a8d7b in ecma_gc_set_object_visited.lto_priv.626 (object_p=0x93939390, is_visited=true) at /afl/jerryscript/jerry-core/ecma/base/ecma-gc.c:111
111 object_p->type_flags_refs = (uint16_t) (object_p->type_flags_refs | ECMA_OBJECT_FLAG_GC_VISITED);
(gdb) where
#0 0x080a8d7b in ecma_gc_set_object_visited.lto_priv.626 (object_p=0x93939390, is_visited=true) at /afl/jerryscript/jerry-core/ecma/base/ecma-gc.c:111
#1 0x080a2740 in ecma_gc_mark (object_p=0x82d7cf8) at /afl/jerryscript/jerry-core/ecma/base/ecma-gc.c:348
#2 0x080a3063 in ecma_gc_run (severity=JMEM_FREE_UNUSED_MEMORY_SEVERITY_LOW) at /afl/jerryscript/jerry-core/ecma/base/ecma-gc.c:732
#3 0x080a322f in ecma_free_unused_memory (severity=JMEM_FREE_UNUSED_MEMORY_SEVERITY_LOW) at /afl/jerryscript/jerry-core/ecma/base/ecma-gc.c:828
#4 0x0808be3d in jmem_run_free_unused_memory_callbacks (severity=JMEM_FREE_UNUSED_MEMORY_SEVERITY_LOW) at /afl/jerryscript/jerry-core/jmem/jmem-allocator.c:148
#5 0x0808c0c1 in jmem_heap_gc_and_alloc_block (size=8, ret_null_on_error=false) at /afl/jerryscript/jerry-core/jmem/jmem-heap.c:359
#6 0x0808c184 in jmem_heap_alloc_block (size=8) at /afl/jerryscript/jerry-core/jmem/jmem-heap.c:408
#7 0x0808bed8 in jmem_pools_alloc (size=8) at /afl/jerryscript/jerry-core/jmem/jmem-poolman.c:102
#8 0x0809bfc5 in ecma_alloc_number () at /afl/jerryscript/jerry-core/ecma/base/ecma-alloc.c:83
#9 0x0809b30c in ecma_create_float_number (ecma_number=0.10000000000000001) at /afl/jerryscript/jerry-core/ecma/base/ecma-helpers-value.c:384
#10 0x0809b970 in ecma_copy_value (value=137201113) at /afl/jerryscript/jerry-core/ecma/base/ecma-helpers-value.c:639
#11 0x0809ba88 in ecma_copy_value_if_not_object (value=137201113) at /afl/jerryscript/jerry-core/ecma/base/ecma-helpers-value.c:683
#12 0x08074baa in ecma_builtin_function_prototype_object_bind (this_arg=137186523, arguments_list_p=0xffffd00c, arguments_number=158)
at /afl/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.c:265
#13 0x08074522 in ecma_builtin_function_prototype_dispatch_routine (builtin_routine_id=37, this_arg_value=137186523, arguments_list=0xffffd00c, arguments_number=158)
at /afl/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtin-function-prototype.inc.h:43
#14 0x080996d2 in ecma_builtin_dispatch_routine (builtin_object_id=ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, builtin_routine_id=37, this_arg_value=137186523, arguments_list=0xffffd00c,
arguments_number=158) at /afl/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h:108
#15 0x08099a96 in ecma_builtin_dispatch_call (obj_p=0x82d5730, this_arg_value=137186523, arguments_list_p=0xffffd00c, arguments_list_len=158)
at /afl/jerryscript/jerry-core/ecma/builtin-objects/ecma-builtins.c:844
#16 0x08091fad in ecma_op_function_call (func_obj_p=0x82d5730, this_arg_value=137186523, arguments_list_p=0xffffd00c, arguments_list_len=158)
at /afl/jerryscript/jerry-core/ecma/operations/ecma-function-object.c:458
#17 0x08085cf3 in opfunc_call.lto_priv.408 (frame_ctx_p=0xffffd3c4) at /afl/jerryscript/jerry-core/vm/vm.c:411
#18 0x0807db45 in vm_execute (frame_ctx_p=0xffffd3c4, arg_p=0x0, arg_list_len=0) at /afl/jerryscript/jerry-core/vm/vm.c:2746
#19 0x0807dd0d in vm_run (bytecode_header_p=0x82d6278, this_binding_value=137183339, lex_env_p=0x82d40a0, is_eval_code=false, arg_list_p=0x0, arg_list_len=0)
at /afl/jerryscript/jerry-core/vm/vm.c:2826
#20 0x08085948 in vm_run_global (bytecode_p=0x82d6278) at /afl/jerryscript/jerry-core/vm/vm.c:231
#21 0x080ae336 in jerry_run (func_val=137188227) at /afl/jerryscript/jerry-core/api/jerry.c:425
#22 0x080ad1dd in main (argc=2, argv=0xffffd634) at /afl/jerryscript/jerry-main/main-unix.c:691
ecma_gc_mark (object_p=0x82d7cf8). Well this is easier:
(gdb) print args_p[i]
$7 = 0x93939393
(gdb) print *(ecma_extended_object_t *)(0x82d7cf8)
$10 = {object = {type_flags_refs = 0x75, gc_next_cp = 0x0, property_list_or_bound_object_cp = 0x0, prototype_or_outer_reference_cp = 0x82d4100}, u = {built_in = {id = 0xd8,
length_and_bitset_size = 0x4c, routine_id = 0x82d, instantiated_bitset = {0x9e}}, class_prop = {class_id = 0x4cd8, u = {value = 0x9e, length = 0x9e}}, function = {scope_cp = 0x82d4cd8,
bytecode_cp = 0x9e}, array = {length = 0x82d4cd8, length_prop = 0x9e}, pseudo_array = {type = 0xd8, extra_info = 0x4c, u1 = {length = 0x82d, class_id = 0x82d}, u2 = {lex_env_cp = 0x9e,
arraybuffer = 0x9e}}, bound_function = {target_function = 0x82d4cd8, args_length = 0x9e}, external_handler_cb = 0x82d4cd8}}
So the 0x9e arguments list has come right out of the bytecode:
Breakpoint 7, opfunc_call.lto_priv.408 (frame_ctx_p=0xffffd3c4) at /afl/jerryscript/jerry-core/vm/vm.c:366
366 uint8_t opcode = frame_ctx_p->byte_code_p[0];
(gdb) print opcode
$27 = 0x0
(gdb) step
369 if (opcode >= CBC_CALL0)
(gdb) print opcode
$28 = 0xb2
(gdb) step
375 arguments_list_len = frame_ctx_p->byte_code_p[1];
(gdb) print frame_ctx_p
$29 = (vm_frame_ctx_t ) 0xffffd3c4
(gdb) print frame_ctx_p
$30 = {
bytecode_header_p = 0x82d6278,
byte_code_p = 0x82d678e "\262\236(\025\004\062b\022\020\t\r\t'\f\tC)\023\024\062b2\b)\f\t2b(\033\062",
byte_code_start_p = 0x82d6394 "\001\002\003(\005\274\274\274", <incomplete sequence \314>,
registers_p = 0xffffd000,
stack_top_p = 0xffffd284,
literal_start_p = 0x82d6284,
lex_env_p = 0x82d40a0,
prev_context_p = 0x0,
this_binding = 0x82d406b,
call_block_result = 0x38,
context_depth = 0x0,
is_eval_code = 0x0,
call_operation = 0x1
}
(gdb) print frame_ctx_p->byte_code_p[1];
Invalid character ';' in expression.
(gdb) print frame_ctx_p->byte_code_p[1]
$31 = 0x9e
(gdb)
So now let's watch that... watch ((vm_frame_ctx_t *) 0xffffd3c4)->byte_code_p[1]
Finally some progress:
Hardware watchpoint 4: *0x82d6519
Old value = 0x93939393
New value = 0x9393939e
parser_post_processing (context_p=0xffffd2b8) at /afl/jerryscript/jerry-core/parser/js/js-parser.c:1784
1784 real_offset++;
(gdb)
The value for the arguments length is passed in via the bytecode - it's an opcode (the next one!) and it's stored in js-parser.c, on line 1711:
1710 /* Storing the opcode */
1711 *dst_p++ = opcode;
(gdb) print opcode
$32 = CBC_CALL_PROP_PUSH_RESULT
Okay, so it's something intrinsic with how the types match up between the compiler and the garbage collector.
Other triggers for the same bug:
Function.prototype.bind({0:0},300000000000000000000000,{0:function(){(0)}});
and
l=eval.bind(.1,function(){},800-9900,function(){},909,910,function(){},900,9000,990,908==901)
and
try{String(Number.MAX_VALUE)}catch(r){}assert("a"=="".replace(/$/,"a")),assert("a"=="".replace(/^/,"a"));var eval=eval.bind(.1,0==function(){})
and
y=assert(isNaN(RegExp("")));eval.bind(.1,0,function Error(){},8==9.18,99.100-998399.100,97100,9998,99.100,998,99.100-9980,989,6998,9100,9998,99.100,99,999,9998,99.100,998,99.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,99.18,99.100,99,999,6998,9100,9998,99.100,99,999,9998.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,99.100-100,99839999,9998,99.100,998,99.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,eval.bind(.1,0,function Error(){},8==9.18,99.100-998399.100,9998,99.100,function x(){},99.100,989,6998,9100,9998,99.100,99,999,9998,99.100,998,99.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,99.18,99.100,99,999,6998,9100,9998,99.100,99,999,9998,99.100,998,99.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,99.100-100,99839999,9998,99.100,998,99.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,99.18,99.100,99,999,6998,9100,9998,99.100,99,999,9998,99.100,998,99.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,99.100-100,998399-100,9998,99.100,99,999,6998,99.100,898,99.+00,999.100,999.100,98==9100,9998,93.100,99,994,9998,99.100,9988,99.10000,9998998,99.100,9988,99.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,99.1098399-100,998399-100,9998100,999.100,998,99.100-100,99839999,9998,99.100,998,99.10000,9998,99.100,function x(){},999.100,998399-100,9998,99.100,99,999,6998,99.100,998,99.1100,98.100,99,9998,99.100,9),99.18,99.100,99,999,6998,9100,9998,99.100,99,999,9998,99.100,998,99.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,99.100-100,998399-100,9998,99.100,99,999,6998,99.100,898,99.+00,999.100,999.100,98==9100,9998,99.100,99,994,9998,99.100,9988,99.10000,9998998,99.100,9988,99.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,99.1098399-100,998399-100,9998100,999.100,999.100,function x(){},999.100,99910,99,9998,99.10098,99.100,999.1,999.1998399-100,998399-100,9998,99.100,99,999,6998,99.100,998,99.1100,98.100,99,9998,99.100,9),9998,99.100,function x(){},999.100,999100,99,9999.100,9998,9999.100,9998,99.100,function x(){},999.100,99910,99,9998,99.100,999.100,9998,99.100,998399-100,998399-100,9998,99.1009,9998,99.100,999.100,9998,99.18,99.100,99,999,6998,9100,9998,99.100,99,999,9998.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,99.100-100,99839999,9998,99.100,998,99.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,eval.bind(.1,0,function Error(){},8==9.18,99.100-998399.100,9998,99.100,function x(){},99.100,989,6998,9100,9998,99.100,99,999,9998,99.100,998,99.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,99.18,99.100,99,999,6998,9100,9998,99.100,99,999,9998,99.100,998,99.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,99.100-100,99839999,9998,99.100,998,99.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,99.18,99.100,99,999,6998,9100,9998,99.100,99,999,9998,99.100,998,99.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,99.100-100,998399-100,9998,99.100,99,999,6998,99.100,898,99.+00,999.100,999.100,98==9100,9998,93.100,99,994,9998,99.100,9988,99.10000,9998998,99.100,9988,99.10000,9998,99.100,function x(){},999.100,999100,99,9998,99.100,999.100,9998,99.1098399-100,998399-100,9998100,999.100,998,99.100-100,99839999,9998,99.100,998,99.10000,9998,99.100,function x(){},999.100,998399-100,9998,99.100,99,999,6998,99.100,998,99.1100,98.100,99,9998,99.100,9),99.18,99(999,6998,99.100,998,99.100,999.99,9998,99.100,function x(){},999.100,999100,99,9,9998,99.100,99,994,1)