Commit message: None
CVE: CVE-2012-5140
CWE: CWE-416
Vulnerability description: Use-after-free vulnerability in Google Chrome before 23.0.1271.97 allows remote attackers to cause a denial of service or possibly have unspecified other impact via vectors related to the URL loader.

Project: Chrome

void PPB_URLLoader_Impl::Close() {
  if (loader_.get())
    loader_->cancel();
  else if (main_document_loader_)
    GetFrameForResource(this)->stopLoading();
}

Project: Chrome

int32_t PPB_URLLoader_Impl::FinishStreamingToFile(
    scoped_refptr<TrackedCallback> callback) {
  int32_t rv = ValidateCallback(callback);
  if (rv != PP_OK)
    return rv;
  if (!response_info_ || !response_info_->body())
    return PP_ERROR_FAILED;

  if (done_status_ != PP_OK_COMPLETIONPENDING)
    return done_status_;

  is_streaming_to_file_ = true;
  if (is_asynchronous_load_suspended_)
    SetDefersLoading(false);

  RegisterCallback(callback);
  return PP_OK_COMPLETIONPENDING;
}

Project: Chrome

int32_t PPB_URLLoader_Impl::FollowRedirect(
    scoped_refptr<TrackedCallback> callback) {
  int32_t rv = ValidateCallback(callback);
  if (rv != PP_OK)
    return rv;

  SetDefersLoading(false);  // Allow the redirect to continue.
  RegisterCallback(callback);
  return PP_OK_COMPLETIONPENDING;
}

Project: Chrome

void PPB_URLLoader_Impl::InstanceWasDeleted() {
  loader_.reset();
}

Project: Chrome

int32_t PPB_URLLoader_Impl::Open(PP_Resource request_id,
                                 scoped_refptr<TrackedCallback> callback) {
  EnterResourceNoLock<PPB_URLRequestInfo_API> enter_request(request_id, true);
  if (enter_request.failed()) {
    Log(PP_LOGLEVEL_ERROR,
        "PPB_URLLoader.Open: invalid request resource ID. (Hint to C++ wrapper"
        " users: use the ResourceRequest constructor that takes an instance or"
        " else the request will be null.)");
    return PP_ERROR_BADARGUMENT;
  }
  return Open(enter_request.object()->GetData(), 0, callback);
}

Project: Chrome

int32_t PPB_URLLoader_Impl::Open(
    const ::ppapi::URLRequestInfoData& request_data,
    int requestor_pid,
    scoped_refptr<TrackedCallback> callback) {
  if (main_document_loader_)
    return PP_ERROR_INPROGRESS;

  int32_t rv = ValidateCallback(callback);
  if (rv != PP_OK)
    return rv;

  ::ppapi::URLRequestInfoData filled_in_request_data = request_data;

  if (URLRequestRequiresUniversalAccess(filled_in_request_data) &&
      !has_universal_access_) {
    Log(PP_LOGLEVEL_ERROR, "PPB_URLLoader.Open: The URL you're requesting is "
        " on a different security origin than your plugin. To request "
        " cross-origin resources, see "
        " PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS.");
    return PP_ERROR_NOACCESS;
  }

  if (loader_.get())
    return PP_ERROR_INPROGRESS;

  WebFrame* frame = GetFrameForResource(this);
  if (!frame)
    return PP_ERROR_FAILED;
  WebURLRequest web_request;
  if (!CreateWebURLRequest(&filled_in_request_data, frame, &web_request))
    return PP_ERROR_FAILED;
  web_request.setRequestorProcessID(requestor_pid);

  request_data_ = filled_in_request_data;

  WebURLLoaderOptions options;
  if (has_universal_access_) {
    options.allowCredentials = true;
    options.crossOriginRequestPolicy =
        WebURLLoaderOptions::CrossOriginRequestPolicyAllow;
  } else {
    options.untrustedHTTP = true;
    if (request_data_.allow_cross_origin_requests) {
      options.allowCredentials = request_data_.allow_credentials;
      options.crossOriginRequestPolicy =
          WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
    } else {
      options.allowCredentials = true;
    }
  }

  is_asynchronous_load_suspended_ = false;
  loader_.reset(frame->createAssociatedURLLoader(options));
  if (!loader_.get())
    return PP_ERROR_FAILED;

  loader_->loadAsynchronously(web_request, this);

  RegisterCallback(callback);
  return PP_OK_COMPLETIONPENDING;
}

Project: Chrome

int32_t PPB_URLLoader_Impl::ReadResponseBody(
    void* buffer,
    int32_t bytes_to_read,
    scoped_refptr<TrackedCallback> callback) {
  int32_t rv = ValidateCallback(callback);
  if (rv != PP_OK)
    return rv;
  if (!response_info_ || response_info_->body())
    return PP_ERROR_FAILED;
  if (bytes_to_read <= 0 || !buffer)
    return PP_ERROR_BADARGUMENT;

  user_buffer_ = static_cast<char*>(buffer);
  user_buffer_size_ = bytes_to_read;

  if (!buffer_.empty())
    return FillUserBuffer();

  if (done_status_ != PP_OK_COMPLETIONPENDING) {
    user_buffer_ = NULL;
    user_buffer_size_ = 0;
    return done_status_;
  }

  RegisterCallback(callback);
  return PP_OK_COMPLETIONPENDING;
}

Project: Chrome

bool PPB_URLLoader_Impl::RecordUploadProgress() const {
  return request_data_.record_upload_progress;
}

Project: Chrome

void PPB_URLLoader_Impl::RunCallback(int32_t result) {
  if (!pending_callback_.get()) {
    CHECK(main_document_loader_);
    return;
  }

  DCHECK(!user_buffer_);

  user_buffer_ = NULL;
  user_buffer_size_ = 0;
  pending_callback_->Run(result);
}

Project: Chrome

void PPB_URLLoader_Impl::SaveResponse(const WebURLResponse& response) {
  scoped_refptr<PPB_URLResponseInfo_Impl> response_info(
      new PPB_URLResponseInfo_Impl(pp_instance()));
  if (response_info->Initialize(response))
    response_info_ = response_info;
}

Project: Chrome

void PPB_URLLoader_Impl::SetDefersLoading(bool defers_loading) {
  if (loader_.get()) {
    loader_->setDefersLoading(defers_loading);
    is_asynchronous_load_suspended_ = defers_loading;
  }

}

Project: Chrome

void PPB_URLLoader_Impl::UpdateStatus() {
  if (status_callback_ &&
      (RecordDownloadProgress() || RecordUploadProgress())) {
    status_callback_(
        pp_instance(), pp_resource(),
        RecordUploadProgress() ? bytes_sent_ : -1,
        RecordUploadProgress() ?  total_bytes_to_be_sent_ : -1,
        RecordDownloadProgress() ? bytes_received_ : -1,
        RecordDownloadProgress() ? total_bytes_to_be_received_ : -1);
  }
}

Project: Chrome

int32_t PPB_URLLoader_Impl::ValidateCallback(
    scoped_refptr<TrackedCallback> callback) {
  DCHECK(callback);

  if (TrackedCallback::IsPending(pending_callback_))
    return PP_ERROR_INPROGRESS;

  return PP_OK;
}

Project: Chrome

void PPB_URLLoader_Impl::didFail(WebURLLoader* loader,
                                 const WebURLError& error) {
  int32_t pp_error = PP_ERROR_FAILED;
  if (error.domain.equals(WebString::fromUTF8(net::kErrorDomain))) {
    switch (error.reason) {
      case net::ERR_ABORTED:
        pp_error = PP_ERROR_ABORTED;
        break;
      case net::ERR_ACCESS_DENIED:
      case net::ERR_NETWORK_ACCESS_DENIED:
        pp_error = PP_ERROR_NOACCESS;
        break;
    }
  } else {
    pp_error = PP_ERROR_NOACCESS;
  }

  FinishLoading(pp_error);
}

Project: Chrome

void PPB_URLLoader_Impl::didFinishLoading(WebURLLoader* loader,
                                          double finish_time) {
  FinishLoading(PP_OK);
}

Project: Chrome

void PPB_URLLoader_Impl::willSendRequest(
    WebURLLoader* loader,
    WebURLRequest& new_request,
    const WebURLResponse& redirect_response) {
  if (!request_data_.follow_redirects) {
    SaveResponse(redirect_response);
    SetDefersLoading(true);
    RunCallback(PP_OK);
  }
}