jerryscript-project/jerryscript

Four issues found through grammar-based fuzzing

voidptr127 opened this issue · 0 comments

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.