Commit message: None
CVE: CVE-2017-15411
CWE: CWE-416
Vulnerability description: Use after free in PDFium in Google Chrome prior to 63.0.3239.84 allowed a remote attacker to potentially exploit heap corruption via a crafted PDF file.

Project: Chrome

void DumpProcess(base::ProcessId pid, const base::FilePath& dumpfile_path) {
  profiling::ProfilingProcessHost* pph =
      profiling::ProfilingProcessHost::GetInstance();
  base::RunLoop run_loop;
  pph->RequestProcessDump(
      pid, dumpfile_path,
      base::BindOnce(&base::RunLoop::Quit, base::Unretained(&run_loop)));
  run_loop.Run();
}

Project: Chrome

base::Value* FindHeapsV2(base::ProcessId pid, base::Value* dump_json) {
  base::Value* events = dump_json->FindKey("traceEvents");
  base::Value* dumps = nullptr;
  base::Value* heaps_v2 = nullptr;
  for (base::Value& event : events->GetList()) {
    const base::Value* found_name =
        event.FindKeyOfType("name", base::Value::Type::STRING);
    if (!found_name)
      continue;
    if (found_name->GetString() != "periodic_interval")
      continue;
    const base::Value* found_pid =
        event.FindKeyOfType("pid", base::Value::Type::INTEGER);
    if (!found_pid)
      continue;
    if (static_cast<base::ProcessId>(found_pid->GetInt()) != pid)
      continue;
    dumps = &event;
    heaps_v2 = dumps->FindPath({"args", "dumps", "heaps_v2"});
    if (heaps_v2)
      return heaps_v2;
  }
  return nullptr;
}

Project: Chrome

int NumProcessesWithName(base::Value* dump_json, std::string name) {
  base::Value* events = dump_json->FindKey("traceEvents");
  int num_found = 0;
  for (base::Value& event : events->GetList()) {
    const base::Value* found_name =
        event.FindKeyOfType("name", base::Value::Type::STRING);
    if (!found_name)
      continue;
    if (found_name->GetString() != "process_name")
      continue;
    const base::Value* found_args =
        event.FindKeyOfType("args", base::Value::Type::DICTIONARY);
    if (!found_args)
      continue;
    const base::Value* found_process_name =
        found_args->FindKeyOfType("name", base::Value::Type::STRING);
    if (!found_process_name)
      continue;
    if (found_process_name->GetString() != name)
      continue;
    num_found++;
  }

  return num_found;
}

Project: Chrome

std::unique_ptr<base::Value> ReadDumpFile(const base::FilePath& path) {
  using base::File;
  File dumpfile(path, File::FLAG_OPEN | File::FLAG_READ);

#if defined(OS_WIN)
  int fd = _open_osfhandle(
      reinterpret_cast<intptr_t>(dumpfile.TakePlatformFile()), 0);
#else
  int fd = dumpfile.TakePlatformFile();
#endif
  gzFile gz_file = gzdopen(fd, "r");
  if (!gz_file) {
    LOG(ERROR) << "Cannot open compressed trace file";
    return nullptr;
  }

  std::string dump_string;

  char buf[4096];
  int bytes_read;
  while ((bytes_read = gzread(gz_file, buf, sizeof(buf))) == sizeof(buf)) {
    dump_string.append(buf, bytes_read);
  }
  if (bytes_read < 0) {
    LOG(ERROR) << "Error reading file";
    return nullptr;
  }
  dump_string.append(buf, bytes_read);  // Grab last bytes.
  if (dump_string.size() == 0) {
    return nullptr;
  }

  return base::JSONReader::Read(dump_string);
}

Project: Chrome

  void ValidateBrowserAllocations(base::Value* dump_json) {
    SCOPED_TRACE("Validating Browser Allocations");
    base::Value* heaps_v2 =
        FindHeapsV2(base::Process::Current().Pid(), dump_json);

    if (GetParam() == switches::kMemlogModeAll ||
        GetParam() == switches::kMemlogModeBrowser ||
        GetParam() == switches::kMemlogModeMinimal) {
      ASSERT_TRUE(heaps_v2);
      ASSERT_NO_FATAL_FAILURE(
          ValidateDump(heaps_v2, kBrowserAllocSize * kBrowserAllocCount,
                       kBrowserAllocCount, "malloc", nullptr));
      ASSERT_NO_FATAL_FAILURE(
          ValidateDump(heaps_v2, total_variadic_allocations_,
                       kBrowserAllocCount, "malloc", nullptr));
      ASSERT_NO_FATAL_FAILURE(ValidateDump(
          heaps_v2, kPartitionAllocSize * kPartitionAllocCount,
          kPartitionAllocCount, "partition_alloc", kPartitionAllocTypeName));
    } else {
      ASSERT_FALSE(heaps_v2) << "There should be no heap dump for the browser.";
    }

    EXPECT_EQ(1, NumProcessesWithName(dump_json, "Browser"));
  }

Project: Chrome

void ValidateDump(base::Value* heaps_v2,
                  int expected_alloc_size,
                  int expected_alloc_count,
                  const char* allocator_name,
                  const char* type_name) {
  base::Value* sizes =
      heaps_v2->FindPath({"allocators", allocator_name, "sizes"});
  ASSERT_TRUE(sizes);
  const base::Value::ListStorage& sizes_list = sizes->GetList();
  EXPECT_FALSE(sizes_list.empty());

  base::Value* counts =
      heaps_v2->FindPath({"allocators", allocator_name, "counts"});
  ASSERT_TRUE(counts);
  const base::Value::ListStorage& counts_list = counts->GetList();
  EXPECT_EQ(sizes_list.size(), counts_list.size());

  base::Value* types =
      heaps_v2->FindPath({"allocators", allocator_name, "types"});
  ASSERT_TRUE(types);
  const base::Value::ListStorage& types_list = types->GetList();
  EXPECT_FALSE(types_list.empty());
  EXPECT_EQ(sizes_list.size(), types_list.size());

  bool found_browser_alloc = false;
  size_t browser_alloc_index = 0;
  for (size_t i = 0; i < sizes_list.size(); i++) {
    if (counts_list[i].GetInt() == expected_alloc_count &&
        sizes_list[i].GetInt() != expected_alloc_size) {
      LOG(WARNING) << "Allocation candidate (size:" << sizes_list[i].GetInt()
                   << " count:" << counts_list[i].GetInt() << ")";
    }
    if (sizes_list[i].GetInt() == expected_alloc_size &&
        counts_list[i].GetInt() == expected_alloc_count) {
      browser_alloc_index = i;
      found_browser_alloc = true;
      break;
    }
  }

  ASSERT_TRUE(found_browser_alloc)
      << "Failed to find an allocation of the "
         "appropriate size. Did the send buffer "
         "not flush? (size: "
      << expected_alloc_size << " count:" << expected_alloc_count << ")";

  if (type_name) {
    bool found = false;
    int type = types_list[browser_alloc_index].GetInt();
    base::Value* strings = heaps_v2->FindPath({"maps", "strings"});
    for (base::Value& dict : strings->GetList()) {
      int id = dict.FindKey("id")->GetInt();
      if (id == type) {
        found = true;
        std::string name = dict.FindKey("string")->GetString();
        EXPECT_STREQ(name.c_str(), type_name);
        break;
      }
    }
    EXPECT_TRUE(found) << "Failed to find type name string.";
  }
}

Project: Chrome

  void ValidateRendererAllocations(base::Value* dump_json) {
    SCOPED_TRACE("Validating Renderer Allocation");
    base::ProcessId renderer_pid = base::GetProcId(browser()
                                                       ->tab_strip_model()
                                                       ->GetActiveWebContents()
                                                       ->GetMainFrame()
                                                       ->GetProcess()
                                                       ->GetHandle());
    base::Value* heaps_v2 = FindHeapsV2(renderer_pid, dump_json);
    if (GetParam() == switches::kMemlogModeAll ||
        GetParam() == switches::kMemlogModeRendererSampling) {
      ASSERT_TRUE(heaps_v2);

    } else {
      ASSERT_FALSE(heaps_v2)
          << "There should be no heap dump for the renderer.";
    }

    if (GetParam() == switches::kMemlogModeRendererSampling) {
      EXPECT_EQ(1, NumProcessesWithName(dump_json, "Renderer"));
    } else {
      EXPECT_GT(NumProcessesWithName(dump_json, "Renderer"), 0);
    }
  }

Project: Chrome

void ProfilingProcessHost::BrowserChildProcessLaunchedAndConnected(
    const content::ChildProcessData& data) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));

  DCHECK_NE(data.process_type, content::ProcessType::PROCESS_TYPE_RENDERER);

  if (!ShouldProfileProcessType(data.process_type)) {
    return;
  }

  profiling::mojom::ProcessType process_type =
      (data.process_type == content::ProcessType::PROCESS_TYPE_GPU)
          ? profiling::mojom::ProcessType::GPU
          : profiling::mojom::ProcessType::OTHER;

  content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO)
      ->PostTask(
          FROM_HERE,
          base::BindOnce(&ProfilingProcessHost::StartProfilingNonRendererChild,
                         base::Unretained(this), data.id,
                         base::GetProcId(data.handle), process_type));
}

Project: Chrome

base::trace_event::TraceConfig GetBackgroundTracingConfig() {
  base::trace_event::TraceConfig trace_config(
      "-*,disabled-by-default-memory-infra",
      base::trace_event::RECORD_UNTIL_FULL);

  trace_config.EnableArgumentFilter();

  base::trace_event::TraceConfig::MemoryDumpConfig memory_config;
  memory_config.allowed_dump_modes.insert(
      base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND);
  base::trace_event::TraceConfig::MemoryDumpConfig::Trigger trigger;
  trigger.min_time_between_dumps_ms = 1 << 30;
  trigger.level_of_detail =
      base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND;
  trigger.trigger_type = base::trace_event::MemoryDumpType::PERIODIC_INTERVAL;
  memory_config.triggers.clear();
  memory_config.triggers.push_back(trigger);
  trace_config.ResetMemoryDumpConfig(memory_config);
  return trace_config;
}

Project: Chrome

ProfilingProcessHost* ProfilingProcessHost::GetInstance() {
  return base::Singleton<
      ProfilingProcessHost,
      base::LeakySingletonTraits<ProfilingProcessHost>>::get();
}

Project: Chrome

ProfilingProcessHost::GetMetadataJSONForTrace() {
  std::unique_ptr<base::DictionaryValue> metadata_dict(
      new base::DictionaryValue);
  metadata_dict->SetKey(
      "product-version",
      base::Value(version_info::GetProductNameAndVersionForUserAgent()));
  metadata_dict->SetKey("user-agent", base::Value(GetUserAgent()));
  metadata_dict->SetKey("os-name",
                        base::Value(base::SysInfo::OperatingSystemName()));
  metadata_dict->SetKey(
      "command_line",
      base::Value(
          base::CommandLine::ForCurrentProcess()->GetCommandLineString()));
  metadata_dict->SetKey(
      "os-arch", base::Value(base::SysInfo::OperatingSystemArchitecture()));
  return metadata_dict;
}

Project: Chrome

void ProfilingProcessHost::GetOutputFileOnBlockingThread(
    base::ProcessId pid,
    base::FilePath dest,
    std::string trigger_name,
    base::OnceClosure done) {
  base::ScopedClosureRunner done_runner(std::move(done));
  base::File file(dest,
                  base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
  content::BrowserThread::PostTask(
      content::BrowserThread::IO, FROM_HERE,
      base::BindOnce(&ProfilingProcessHost::HandleDumpProcessOnIOThread,
                     base::Unretained(this), pid, std::move(dest),
                     std::move(file), std::move(trigger_name),
                     done_runner.Release()));
}

Project: Chrome

void ProfilingProcessHost::MakeConnector(
    content::ServiceManagerConnection* connection) {
  connector_ = connection->GetConnector()->Clone();
}

Project: Chrome

void ProfilingProcessHost::Observe(
    int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
  content::RenderProcessHost* host =
      content::Source<content::RenderProcessHost>(source).ptr();

  if (host == profiled_renderer_ &&
      (type == content::NOTIFICATION_RENDERER_PROCESS_TERMINATED ||
       type == content::NOTIFICATION_RENDERER_PROCESS_CLOSED)) {
    DCHECK_EQ(mode(), Mode::kRendererSampling);
    profiled_renderer_ = nullptr;
  }
  if (type == content::NOTIFICATION_RENDERER_PROCESS_CREATED &&
      ShouldProfileNewRenderer(host)) {
    if (mode() == Mode::kRendererSampling) {
      profiled_renderer_ = host;
    }
    ProfilingClientBinder client(host);

    content::BrowserThread::GetTaskRunnerForThread(content::BrowserThread::IO)
        ->PostTask(
            FROM_HERE,
            base::BindOnce(&ProfilingProcessHost::AddClientToProfilingService,
                           base::Unretained(this), client.take(),
                           base::GetProcId(host->GetHandle()),
                           profiling::mojom::ProcessType::RENDERER));
  }
}

Project: Chrome

void ProfilingProcessHost::OnDumpProcessesForTracingCallback(
    uint64_t guid,
    std::vector<profiling::mojom::SharedBufferWithSizePtr> buffers) {
  for (auto& buffer_ptr : buffers) {
    mojo::ScopedSharedBufferHandle& buffer = buffer_ptr->buffer;
    uint32_t size = buffer_ptr->size;

    if (!buffer->is_valid())
      return;

    mojo::ScopedSharedBufferMapping mapping = buffer->Map(size);
    if (!mapping) {
      DLOG(ERROR) << "Failed to map buffer";
      return;
    }

    const char* char_buffer = static_cast<const char*>(mapping.get());
    std::string json(char_buffer, char_buffer + size);

    const int kTraceEventNumArgs = 1;
    const char* const kTraceEventArgNames[] = {"dumps"};
    const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE};
    std::unique_ptr<base::trace_event::ConvertableToTraceFormat> wrapper(
        new StringWrapper(std::move(json)));

    TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_PROCESS_ID(
        TRACE_EVENT_PHASE_MEMORY_DUMP,
        base::trace_event::TraceLog::GetCategoryGroupEnabled(
            base::trace_event::MemoryDumpManager::kTraceCategory),
        "periodic_interval", trace_event_internal::kGlobalScope, guid,
        buffer_ptr->pid, kTraceEventNumArgs, kTraceEventArgNames,
        kTraceEventArgTypes, nullptr /* arg_values */, &wrapper,
        TRACE_EVENT_FLAG_HAS_ID);
  }
  if (dump_process_for_tracing_callback_) {
    std::move(dump_process_for_tracing_callback_).Run();
    dump_process_for_tracing_callback_.Reset();
  }
}

Project: Chrome

void ProfilingProcessHost::OnProcessDumpComplete(base::FilePath file_path,
                                                 std::string trigger_name,
                                                 base::OnceClosure done,
                                                 bool success) {
  base::ScopedClosureRunner done_runner(std::move(done));
  if (!success) {
    DLOG(ERROR) << "Cannot dump process.";
    base::PostTaskWithTraits(
        FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
        base::BindOnce(base::IgnoreResult(&base::DeleteFile), file_path,
                       false));
    return;
  }
}

Project: Chrome

void OnTraceUploadComplete(TraceCrashServiceUploader* uploader,
                           bool success,
                           const std::string& feedback) {
  if (!success) {
    LOG(ERROR) << "Cannot upload trace file: " << feedback;
    return;
  }

  LOG(WARNING) << "slow-reports sent: '" << feedback << '"';
}

Project: Chrome

  explicit ProfilingClientBinder(content::BrowserChildProcessHost* host)
      : ProfilingClientBinder() {
    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    content::BindInterface(host->GetHost(), std::move(request_));
  }

Project: Chrome

  explicit ProfilingClientBinder(content::RenderProcessHost* host)
      : ProfilingClientBinder() {
    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    content::BindInterface(host, std::move(request_));
  }

Project: Chrome

  explicit ProfilingClientBinder(service_manager::Connector* connector)
      : ProfilingClientBinder() {
    connector->BindInterface(content::mojom::kBrowserServiceName,
                             std::move(request_));
  }

Project: Chrome

  ProfilingClientBinder()
      : memlog_client_(), request_(mojo::MakeRequest(&memlog_client_)) {}

Project: Chrome

ProfilingProcessHost::ProfilingProcessHost()
    : is_registered_(false),
      background_triggers_(this),
      mode_(Mode::kNone),
      profiled_renderer_(nullptr),
      always_sample_for_tests_(false) {}

Project: Chrome

void ProfilingProcessHost::Register() {
  DCHECK(!is_registered_);
  Add(this);
  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
                 content::NotificationService::AllBrowserContextsAndSources());
  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
                 content::NotificationService::AllBrowserContextsAndSources());
  registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
                 content::NotificationService::AllBrowserContextsAndSources());
  is_registered_ = true;
}

Project: Chrome

void ProfilingProcessHost::RequestProcessDump(base::ProcessId pid,
                                              base::FilePath dest,
                                              base::OnceClosure done) {
  if (!connector_) {
    DLOG(ERROR)
        << "Requesting process dump when profiling process hasn't started.";
    return;
  }

  base::PostTaskWithTraits(
      FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
      base::BindOnce(&ProfilingProcessHost::GetOutputFileOnBlockingThread,
                     base::Unretained(this), pid, std::move(dest),
                     kNoTriggerName, std::move(done)));
}

Project: Chrome

void ProfilingProcessHost::SetDumpProcessForTracingCallback(
    base::OnceClosure callback) {
  DCHECK(!dump_process_for_tracing_callback_);
  dump_process_for_tracing_callback_ = std::move(callback);
}

Project: Chrome

void ProfilingProcessHost::SetMode(Mode mode) {
  mode_ = mode;
}

Project: Chrome

void ProfilingProcessHost::SetRendererSamplingAlwaysProfileForTest() {
  always_sample_for_tests_ = true;
}

Project: Chrome

bool ProfilingProcessHost::ShouldProfileNewRenderer(
    content::RenderProcessHost* renderer) const {
  if (Profile::FromBrowserContext(renderer->GetBrowserContext())
          ->GetProfileType() == Profile::INCOGNITO_PROFILE) {
    return false;
  }

  if (mode() == Mode::kAll) {
    return true;
  } else if (mode() == Mode::kRendererSampling && !profiled_renderer_) {
    if (always_sample_for_tests_) {
      return true;
    }

    return (base::RandUint64() % 100000) < 33333;
  }

  return false;
}

Project: Chrome

bool ProfilingProcessHost::ShouldProfileProcessType(int process_type) {
  switch (mode()) {
    case Mode::kAll:
      return true;

    case Mode::kMinimal:
      return (process_type == content::ProcessType::PROCESS_TYPE_GPU ||
              process_type == content::ProcessType::PROCESS_TYPE_BROWSER);

    case Mode::kGpu:
      return process_type == content::ProcessType::PROCESS_TYPE_GPU;

    case Mode::kBrowser:
      return process_type == content::ProcessType::PROCESS_TYPE_BROWSER;

    case Mode::kRendererSampling:
      return false;

    case Mode::kNone:
      return false;

    case Mode::kCount:
      {}
  }

  NOTREACHED();
  return false;
}

Project: Chrome

void ProfilingProcessHost::StartProfilingNonRendererChild(
    int child_process_id,
    base::ProcessId proc_id,
    profiling::mojom::ProcessType process_type) {
  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));

  content::BrowserChildProcessHost* host =
      content::BrowserChildProcessHost::FromID(child_process_id);
  if (!host)
    return;

  ProfilingClientBinder client(host);
  AddClientToProfilingService(client.take(), proc_id, process_type);
}

Project: Chrome

  explicit StringWrapper(std::string string) : json_(std::move(string)) {}

Project: Chrome

void ProfilingProcessHost::Unregister() {
  DCHECK(is_registered_);
  Remove(this);
}

Project: Chrome

void UploadTraceToCrashServer(std::string file_contents,
                              std::string trigger_name) {
  base::Value rules_list(base::Value::Type::LIST);
  base::Value rule(base::Value::Type::DICTIONARY);
  rule.SetKey("rule", base::Value("MEMLOG"));
  rule.SetKey("trigger_name", base::Value(std::move(trigger_name)));
  rule.SetKey("category", base::Value("BENCHMARK_MEMORY_HEAVY"));
  rules_list.GetList().push_back(std::move(rule));

  base::Value configs(base::Value::Type::DICTIONARY);
  configs.SetKey("mode", base::Value("REACTIVE_TRACING_MODE"));
  configs.SetKey("category", base::Value("MEMLOG"));
  configs.SetKey("configs", std::move(rules_list));

  std::unique_ptr<base::DictionaryValue> metadata =
      base::MakeUnique<base::DictionaryValue>();
  metadata->SetKey("config", std::move(configs));

  TraceCrashServiceUploader* uploader = new TraceCrashServiceUploader(
      g_browser_process->system_request_context());

  uploader->DoUpload(file_contents, content::TraceUploader::COMPRESSED_UPLOAD,
                     std::move(metadata),
                     content::TraceUploader::UploadProgressCallback(),
                     base::Bind(&OnTraceUploadComplete, base::Owned(uploader)));
}

Project: Chrome

  mojom::ProfilingClientPtr take() { return std::move(memlog_client_); }

Project: Chrome

ProfilingProcessHost::~ProfilingProcessHost() {
  if (is_registered_)
    Unregister();
}