Description
I am a researcher in software testing from the University of Stuttgart, Germany. We are testing grammar-based fuzzers and have chosen JerryScript as one of our fuzz targets for JavaScript. During our experiments we found 4 issues with JerryScript that I'd like to share with you.
Please excuse if some of the examples may look weird. This is typically the case when generating random test inputs. I verified crashes/issues by cross-checking the behavior with the JavaScript engines that Firefox or Chrome use. Thus, I am sometimes not entirely sure whether JerryScript or Firefox or Chrome misbehave.
All issues can be split into separate ones if required.
JerryScript revision
3.0.0 - 1a2c04763aba49f52b1537acd3730098c873511c
Build platform
Rocky Linux release 9.1 (Blue Onyx), 5.14.0-162.18.1.el9_1.x86_64
Build steps
python3 tools/build.py
Build logs
-- The C compiler identification is GNU 11.3.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- CMAKE_BUILD_TYPE MinSizeRel
-- CMAKE_C_COMPILER_ID GNU
-- CMAKE_SYSTEM_NAME Linux
-- CMAKE_SYSTEM_PROCESSOR x86_64
-- BUILD_SHARED_LIBS OFF
-- ENABLE_AMALGAM OFF
-- ENABLE_LTO ON
-- ENABLE_STRIP ON
-- ENABLE_COMPILE_COMMANDS ON
-- JERRY_VERSION 3.0.0
-- JERRY_CMDLINE ON
-- JERRY_CMDLINE_TEST OFF
-- JERRY_CMDLINE_SNAPSHOT OFF
-- JERRY_LIBFUZZER OFF (FORCED BY COMPILER)
-- JERRY_PORT ON (FORCED BY CMDLINE OR LIBFUZZER OR TESTS)
-- JERRY_EXT ON (FORCED BY CMDLINE OR TESTS)
-- JERRY_MATH OFF
-- UNITTESTS OFF
-- DOCTESTS OFF
-- JERRY_CPOINTER_32_BIT OFF
-- JERRY_DEBUGGER OFF
-- JERRY_ERROR_MESSAGES OFF
-- JERRY_EXTERNAL_CONTEXT OFF
-- JERRY_PARSER ON
-- JERRY_FUNCTION_TO_STRING OFF
-- JERRY_LINE_INFO OFF
-- JERRY_LOGGING OFF
-- JERRY_MEM_STATS OFF
-- JERRY_MEM_GC_BEFORE_EACH_ALLOC OFF
-- JERRY_PARSER_DUMP_BYTE_CODE OFF
-- JERRY_PROFILE es.next
-- JERRY_PROMISE_CALLBACK OFF
-- JERRY_REGEXP_STRICT_MODE OFF
-- JERRY_REGEXP_DUMP_BYTE_CODE OFF
-- JERRY_SNAPSHOT_EXEC OFF
-- JERRY_SNAPSHOT_SAVE OFF
-- JERRY_SYSTEM_ALLOCATOR OFF
-- JERRY_VALGRIND OFF
-- JERRY_VM_HALT OFF
-- JERRY_VM_THROW OFF
-- JERRY_GLOBAL_HEAP_SIZE (512)
-- JERRY_GC_LIMIT (0)
-- JERRY_STACK_LIMIT (0)
-- JERRY_GC_MARK_LIMIT (8)
-- Looking for sin in m
-- Looking for sin in m - found
-- FEATURE_INIT_FINI OFF
-- Performing Test HAVE_TM_GMTOFF
-- Performing Test HAVE_TM_GMTOFF - Success
-- ENABLE_LINK_MAP OFF
-- JERRY_TEST_STACK_MEASURE OFF
Issue 1 - Arrays with self-references result in a core dump when printed
Test case
test1.js
a = []
a[0] = 5 /* works */
a[1] = a /* fails! core dump */
Execution
cat test1.js | ./build/bin/jerry
Output
jerry>
jerry> 5
jerry> Aborted (core dumped)
Expected behavior
Should print the array as something like [5, a]
without resulting in a core dump. I suppose that it is an issue with the (console) print function not handling already resolved references correctly? When executing without piping it to the binary it seems to work. Also executing a = []; a[0] = 5; a[1] = a; a[2] = 4; print(a[1][2]);
works and prints 4
. I guess that once a reference in the print statement is resolved for an object, the print should not recurse again?
Issue 2 - Editing the prototype of this
by assigning a Proxy object leads to core dumps in some cases, while in other cases, it doesn't result in a core dump but should print an error
Test case
test2.js
this.__proto__ = new Proxy(Number, this.__proto__); /* works, but shouldn't? */
Number.__proto__ = this.__proto__ = new Proxy(Number, this.__proto__); /* core dump */
Execution
cat test2.js | ./build/bin/jerry
Output
jerry> function () { [native code] }
jerry> Aborted (core dumped)
Expected behavior
I assume that both statements should result in similar messages like for example in Firefox Uncaught TypeError: can't set prototype of this object
or Chrome Uncaught TypeError: Immutable prototype object '#<Window>' cannot have their prototype set
. However, I am not aware whether it has to do something with the fact that this
is something different in the browsers but not in JerryScript.
Issue 3 - Editing the prototype of objects like Number
or Array
by assigning a Proxy leads to a segmentation fault (core dump)
Test case
test3.js
Number.__proto__ = new Proxy(Number, {})
Execution
cat test3.js | ./build/bin/jerry
Output
Segmentation fault (core dumped)
Expected behavior
Should print something like Proxy { <target>: Number(), <handler>: {} }
. I suppose this is similar to issue 1 with resolving recursive references?
Issue 4 - Chained assignments cause in some cases core dumps while working as expected when executed on their own
Test case
test4.js
{}.__proto__[20] = new Array(1000.00) /* works, syntax error */
myvar = {}.__proto__[20] /* works, undefined */
myvar = {}.__proto__[20] = new Array(1000.00) /* core dump */
Execution
cat test4.js | ./build/bin/jerry
Output
jerry> Unhandled exception: SyntaxError
jerry> undefined
jerry> Aborted (core dumped)
Expected behavior
This should result in printing an empty array with 1000 slots (similar to new Array(1000.00)
which works just fine). Although this also only happens when the console print is executed, I think myvar
actually does not get the correct value assigned, i.e. new Array(1000.00)
. However, I am not sure whether ignoring a sub-expression that results in an syntax error should be treated as "non-existent" or whether the whole statement should be discarded in a syntax error.