Commit message: None
CVE: CVE-2016-5216
CWE: CWE-416
Vulnerability description: A use after free in PDFium in Google Chrome prior to 55.0.2883.75 for Mac, Windows and Linux, and 55.0.2883.84 for Android allowed a remote attacker to perform an out of bounds memory read via a crafted PDF file.

Project: Chrome

void PDFiumEngine::FinishLoadingDocument() {
  DCHECK(doc_loader_.IsDocumentComplete() && doc_);

  bool need_update = false;
  for (size_t i = 0; i < pages_.size(); ++i) {
    if (pages_[i]->available())
      continue;

    pages_[i]->set_available(true);
    FPDFAvail_IsPageAvail(fpdf_availability_, i, &download_hints_);
    need_update = true;
    if (IsPageVisible(i))
      client_->Invalidate(GetPageScreenRect(i));
  }
  if (need_update)
    LoadPageInfo(true);

  if (called_do_document_action_)
    return;
  called_do_document_action_ = true;

  FORM_DoDocumentJSAction(form_);
  FORM_DoDocumentOpenAction(form_);
  if (most_visible_page_ != -1) {
    FPDF_PAGE new_page = pages_[most_visible_page_]->GetPage();
    FORM_DoPageAAction(new_page, form_, FPDFPAGE_AACTION_OPEN);
  }

  if (doc_)  // This can only happen if loading |doc_| fails.
    client_->DocumentLoadComplete(pages_.size());
}

Project: Chrome

int PDFiumEngine::Form_Browse(IPDF_JSPLATFORM* param,
                              void* file_path,
                              int length) {
  PDFiumEngine* engine = static_cast<PDFiumEngine*>(param);
  std::string path = engine->client_->ShowFileSelectionDialog();
  if (path.size() + 1 <= static_cast<size_t>(length))
    memcpy(file_path, &path[0], path.size() + 1);
  return path.size() + 1;
}

Project: Chrome

FPDF_LPFILEHANDLER PDFiumEngine::Form_DownloadFromURL(FPDF_FORMFILLINFO* param,
                                                      FPDF_WIDESTRING url) {
  return nullptr;
}

Project: Chrome

void PDFiumEngine::Form_ExecuteNamedAction(FPDF_FORMFILLINFO* param,
                                           FPDF_BYTESTRING named_action) {
  PDFiumEngine* engine = static_cast<PDFiumEngine*>(param);
  std::string action(named_action);
  if (action == "Print") {
    engine->client_->Print();
    return;
  }

  int index = engine->last_page_mouse_down_;
  /* Don't try to calculate the most visible page if we don't have a left click
     before this event (this code originally copied Form_GetCurrentPage which of
     course needs to do that and which doesn't have recursion). This can end up
     causing infinite recursion. See http://crbug.com/240413 for more
     information. Either way, it's not necessary for the spec'd list of named
     actions.
  if (index == -1)
    index = engine->GetMostVisiblePage();
  */
  if (index == -1)
    return;

  if (action == "NextPage") {
    engine->ScrollToPage(index + 1);
  } else if (action == "PrevPage") {
    engine->ScrollToPage(index - 1);
  } else if (action == "FirstPage") {
    engine->ScrollToPage(0);
  } else if (action == "LastPage") {
    engine->ScrollToPage(engine->pages_.size() - 1);
  }
}

Project: Chrome

int PDFiumEngine::Form_GetPlatform(FPDF_FORMFILLINFO* param,
                                   void* platform,
                                   int length) {
  int platform_flag = -1;

#if defined(WIN32)
  platform_flag = 0;
#elif defined(__linux__)
  platform_flag = 1;
#else
  platform_flag = 2;
#endif

  std::string javascript = "alert(\"Platform:"
      + base::DoubleToString(platform_flag)
      + "\")";

  return platform_flag;
}

Project: Chrome

void PDFiumEngine::Form_KillTimer(FPDF_FORMFILLINFO* param, int timer_id) {
  PDFiumEngine* engine = static_cast<PDFiumEngine*>(param);
  engine->timers_.erase(timer_id);
}

Project: Chrome

void PDFiumEngine::Form_OnChange(FPDF_FORMFILLINFO* param) {
}

Project: Chrome

void PDFiumEngine::Form_SubmitForm(IPDF_JSPLATFORM* param,
                                   void* form_data,
                                   int length,
                                   FPDF_WIDESTRING url) {
  std::string url_str =
      base::UTF16ToUTF8(reinterpret_cast<const base::char16*>(url));
  PDFiumEngine* engine = static_cast<PDFiumEngine*>(param);
  engine->client_->SubmitForm(url_str, form_data, length);
}

Project: Chrome

int PDFiumEngine::GetBlock(void* param, unsigned long position,
                           unsigned char* buffer, unsigned long size) {
  DocumentLoader* loader = static_cast<DocumentLoader*>(param);
  return loader->GetBlock(position, size, buffer);
}

Project: Chrome

pp::Buffer_Dev PDFiumEngine::GetFlattenedPrintData(const FPDF_DOCUMENT& doc) {
  pp::Buffer_Dev buffer;
  int page_count = FPDF_GetPageCount(doc);
  for (int i = 0; i < page_count; ++i) {
    FPDF_PAGE page = FPDF_LoadPage(doc, i);
    DCHECK(page);
    int flatten_ret = FPDFPage_Flatten(page, FLAT_PRINT);
    FPDF_ClosePage(page);
    if (flatten_ret == FLATTEN_FAIL)
      return buffer;
  }

  PDFiumMemBufferFileWrite output_file_write;
  if (FPDF_SaveAsCopy(doc, &output_file_write, 0)) {
    buffer = pp::Buffer_Dev(
        client_->GetPluginInstance(), output_file_write.size());
    if (!buffer.is_null()) {
      memcpy(buffer.data(), output_file_write.buffer().c_str(),
             output_file_write.size());
    }
  }
  return buffer;
}

Project: Chrome

int PDFiumEngine::GetNumberOfPages() {
  return pages_.size();
}

Project: Chrome

pp::Rect PDFiumEngine::GetPageContentsRect(int index) {
  return GetScreenRect(pages_[index]->rect());
}

Project: Chrome

pp::Rect PDFiumEngine::GetScreenRect(const pp::Rect& rect) const {
  pp::Rect rv;
  int right =
      static_cast<int>(ceil(rect.right() * current_zoom_ - position_.x()));
  int bottom =
    static_cast<int>(ceil(rect.bottom() * current_zoom_ - position_.y()));

  rv.set_x(static_cast<int>(rect.x() * current_zoom_ - position_.x()));
  rv.set_y(static_cast<int>(rect.y() * current_zoom_ - position_.y()));
  rv.set_width(right - rv.x());
  rv.set_height(bottom - rv.y());
  return rv;
}

Project: Chrome

bool PDFiumEngine::HandleEvent(const pp::InputEvent& event) {
  DCHECK(!defer_page_unload_);
  defer_page_unload_ = true;
  bool rv = false;
  switch (event.GetType()) {
    case PP_INPUTEVENT_TYPE_MOUSEDOWN:
      rv = OnMouseDown(pp::MouseInputEvent(event));
      break;
    case PP_INPUTEVENT_TYPE_MOUSEUP:
      rv = OnMouseUp(pp::MouseInputEvent(event));
      break;
    case PP_INPUTEVENT_TYPE_MOUSEMOVE:
      rv = OnMouseMove(pp::MouseInputEvent(event));
      break;
    case PP_INPUTEVENT_TYPE_KEYDOWN:
      rv = OnKeyDown(pp::KeyboardInputEvent(event));
      break;
    case PP_INPUTEVENT_TYPE_KEYUP:
      rv = OnKeyUp(pp::KeyboardInputEvent(event));
      break;
    case PP_INPUTEVENT_TYPE_CHAR:
      rv = OnChar(pp::KeyboardInputEvent(event));
      break;
    default:
      break;
  }

  DCHECK(defer_page_unload_);
  defer_page_unload_ = false;
  for (int page_index : deferred_page_unloads_)
    pages_[page_index]->Unload();
  deferred_page_unloads_.clear();
  return rv;
}

Project: Chrome

void PDFiumEngine::LoadPageInfo(bool reload) {
  pending_pages_.clear();
  pp::Size old_document_size = document_size_;
  document_size_ = pp::Size();
  std::vector<pp::Rect> page_rects;
  int page_count = FPDF_GetPageCount(doc_);
  bool doc_complete = doc_loader_.IsDocumentComplete();
  bool is_linear = FPDFAvail_IsLinearized(fpdf_availability_) == PDF_LINEARIZED;
  for (int i = 0; i < page_count; ++i) {
    if (i != 0) {
      document_size_.Enlarge(0, kPageSeparatorThickness);
    }

    bool page_available;
    if (reload) {
      page_available = pages_[i]->available();
    } else if (is_linear) {
      int linear_page_avail =
          FPDFAvail_IsPageAvail(fpdf_availability_, i, &download_hints_);
      page_available = linear_page_avail == PDF_DATA_AVAIL;
    } else {
      page_available = doc_complete;
    }

    pp::Size size = page_available ? GetPageSize(i) : default_page_size_;
    size.Enlarge(kPageShadowLeft + kPageShadowRight,
                 kPageShadowTop + kPageShadowBottom);
    pp::Rect rect(pp::Point(0, document_size_.height()), size);
    page_rects.push_back(rect);

    if (size.width() > document_size_.width())
      document_size_.set_width(size.width());

    document_size_.Enlarge(0, size.height());
  }

  for (int i = 0; i < page_count; ++i) {
    page_rects[i].set_x((document_size_.width() - page_rects[i].width()) / 2);
    pp::Rect page_rect(page_rects[i]);
    page_rect.Inset(kPageShadowLeft, kPageShadowTop,
                    kPageShadowRight, kPageShadowBottom);
    if (reload) {
      pages_[i]->set_rect(page_rect);
    } else {
      pages_.push_back(new PDFiumPage(this, i, page_rect, false));
    }
  }

  CalculateVisiblePages();
  if (document_size_ != old_document_size)
    client_->DocumentSizeUpdated(document_size_);
}

Project: Chrome

bool PDFiumEngine::MouseDownState::Matches(
    const PDFiumPage::Area& area,
    const PDFiumPage::LinkTarget& target) const {
  if (area_ != area)
    return false;

  if (area == PDFiumPage::WEBLINK_AREA)
    return target_.url == target.url;

  if (area == PDFiumPage::DOCLINK_AREA)
    return target_.page == target.page;

  return true;
}

Project: Chrome

void PDFiumEngine::OnGetPasswordComplete(int32_t result,
                                         const pp::Var& password) {
  getting_password_ = false;

  std::string password_text;
  if (result == PP_OK && password.is_string())
    password_text = password.AsString();
  ContinueLoadingDocument(password_text);
}

Project: Chrome

bool PDFiumEngine::OnMouseUp(const pp::MouseInputEvent& event) {
  if (event.GetButton() != PP_INPUTEVENT_MOUSEBUTTON_LEFT &&
      event.GetButton() != PP_INPUTEVENT_MOUSEBUTTON_MIDDLE) {
    return false;
  }

  int page_index = -1;
  int char_index = -1;
  int form_type = FPDF_FORMFIELD_UNKNOWN;
  PDFiumPage::LinkTarget target;
  PDFiumPage::Area area =
      GetCharIndex(event, &page_index, &char_index, &form_type, &target);

  if (mouse_down_state_.Matches(area, target)) {
    if (area == PDFiumPage::WEBLINK_AREA) {
      uint32_t modifiers = event.GetModifiers();
      bool middle_button =
          !!(modifiers & PP_INPUTEVENT_MODIFIER_MIDDLEBUTTONDOWN);
      bool alt_key = !!(modifiers & PP_INPUTEVENT_MODIFIER_ALTKEY);
      bool ctrl_key = !!(modifiers & PP_INPUTEVENT_MODIFIER_CONTROLKEY);
      bool meta_key = !!(modifiers & PP_INPUTEVENT_MODIFIER_METAKEY);
      bool shift_key = !!(modifiers & PP_INPUTEVENT_MODIFIER_SHIFTKEY);

      WindowOpenDisposition disposition =
          ui::DispositionFromClick(middle_button, alt_key, ctrl_key, meta_key,
                                   shift_key);

      client_->NavigateTo(target.url, disposition);
      client_->FormTextFieldFocusChange(false);
      return true;
    }
  }

  if (event.GetButton() == PP_INPUTEVENT_MOUSEBUTTON_MIDDLE)
    return false;

  if (page_index != -1) {
    double page_x, page_y;
    pp::Point point = event.GetPosition();
    DeviceToPage(page_index, point.x(), point.y(), &page_x, &page_y);
    FORM_OnLButtonUp(
        form_, pages_[page_index]->GetPage(), 0, page_x, page_y);
  }

  if (!selecting_)
    return false;

  SetSelecting(false);
  return true;
}

Project: Chrome

void PDFiumEngine::OnNewDataAvailable() {
  client_->DocumentLoadProgress(doc_loader_.GetAvailableData(),
                                doc_loader_.document_size());
}

Project: Chrome

void PDFiumEngine::OnPendingRequestComplete() {
  if (!doc_ || !form_) {
    DCHECK(fpdf_availability_);
    LoadDocument();
    return;
  }

  bool update_pages = false;
  std::vector<int> still_pending;
  for (int pending_page : pending_pages_) {
    if (CheckPageAvailable(pending_page, &still_pending)) {
      update_pages = true;
      if (IsPageVisible(pending_page))
        client_->Invalidate(GetPageScreenRect(pending_page));
    }
  }
  pending_pages_.swap(still_pending);
  if (update_pages)
    LoadPageInfo(true);
}

Project: Chrome

void PDFiumEngine::Paint(const pp::Rect& rect,
                         pp::ImageData* image_data,
                         std::vector<pp::Rect>* ready,
                         std::vector<pp::Rect>* pending) {
  DCHECK(image_data);
  DCHECK(ready);
  DCHECK(pending);

  pp::Rect leftover = rect;
  for (size_t i = 0; i < visible_pages_.size(); ++i) {
    int index = visible_pages_[i];
    pp::Rect page_rect = pages_[index]->rect();
    pp::Rect page_rect_in_screen = GetPageScreenRect(index);
    pp::Rect dirty_in_screen = page_rect_in_screen.Intersect(leftover);
    if (dirty_in_screen.IsEmpty())
      continue;

    if (i == 0) {
      pp::Rect blank_space_in_screen = dirty_in_screen;
      blank_space_in_screen.set_y(0);
      blank_space_in_screen.set_height(dirty_in_screen.y());
      leftover = leftover.Subtract(blank_space_in_screen);
    }
    leftover = leftover.Subtract(dirty_in_screen);

    if (pages_[index]->available()) {
      int progressive = GetProgressiveIndex(index);
      if (progressive != -1) {
        DCHECK_GE(progressive, 0);
        DCHECK_LT(static_cast<size_t>(progressive), progressive_paints_.size());
        if (progressive_paints_[progressive].rect != dirty_in_screen) {
          pending->push_back(dirty_in_screen);
          continue;
        }
      }

      if (progressive == -1) {
        progressive = StartPaint(index, dirty_in_screen);
        progressive_paint_timeout_ = kMaxInitialProgressivePaintTimeMs;
      } else {
        progressive_paint_timeout_ = kMaxProgressivePaintTimeMs;
      }

      progressive_paints_[progressive].painted_ = true;
      if (ContinuePaint(progressive, image_data)) {
        FinishPaint(progressive, image_data);
        ready->push_back(dirty_in_screen);
      } else {
        pending->push_back(dirty_in_screen);
      }
    } else {
      PaintUnavailablePage(index, dirty_in_screen, image_data);
      ready->push_back(dirty_in_screen);
    }
  }
}

Project: Chrome

void ShutdownSDK() {
  FPDF_DestroyLibrary();
  TearDownV8();
}

Project: Chrome

void TearDownV8() {
  g_isolate_holder->isolate()->Exit();
  delete g_isolate_holder;
  g_isolate_holder = nullptr;
}