The length of array
arr is 4, and we are returning an element of this array. V8 will perform run-time bounds checking to make sure that the last statement does not access memory outside the bounds of the array. During optimization of such a function, V8 might remove array bounds checking if it concluded that
typer_index is always zero (or, in general, if
typer_index * 10 is provably always inside the bounds of the array). This saves a few more CPU cycles during execution of the optimized function. In the event that JITted code produces an erroneous numeric result, though, it may be possible fool the V8 engine into thinking
typer_index must be zero, while in actuality it will be set to a different (erroneous) value. Then, when the array access is performed, it will trigger an out-of-bounds memory access.
This method was so successful that the V8 developers eventually decided to remove array-bounds-check elimination. See this blog for more information about this exploitation technique, as well as this blog for further discussion.
Since V8 mitigated the array bounds elimination exploitation technique, a new technique is necessary. At Pwn2Own, the contestants used a technique that produces out-of-bounds access via
ArrayPrototypeShift. I was able to trace this method back to late 2020 by searching the Chromium bug tracking system. It was mitigated a week after the Pwn2Own competition by adding a new
CheckBounds node. Here I provide you with a quick analysis of this method:
When a function undergoing optimization has calls to the
Array.shift method, the execution flow eventually reaches the function
JSCallReducer::ReduceArrayPrototypeShift function (see
src/compiler/js-call-reducer.cc). Since a call to the built-in