blob: 75f8af5200c1dc2d3554aaa472899f5bb8bab0a6 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/allocator/scheduler_loop_quarantine_config.h"
#include <string_view>
#include "base/allocator/partition_alloc_features.h"
#include "base/feature_list.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/strings/safe_sprintf.h"
#include "base/strings/strcat.h"
namespace base::allocator {
namespace {
// For configuration purpose use "browser" instead of "" for visibility.
constexpr char kProcessTypeBrowserStr[] = "browser";
constexpr char kProcessTypeWildcardStr[] = "*";
// SchedulerLoopQuarantineBranchType string representation.
constexpr char kBranchTypeGlobalStr[] = "global";
constexpr char kBranchTypeThreadLocalDefaultStr[] = "*";
constexpr char kBranchTypeMainStr[] = "main";
constexpr char kBranchTypeAdvancedMemorySafetyChecksStr[] = "amsc";
constexpr std::string_view GetSchedulerLoopQuarantineBranchTypeStr(
SchedulerLoopQuarantineBranchType type) {
switch (type) {
case SchedulerLoopQuarantineBranchType::kGlobal:
return kBranchTypeGlobalStr;
case SchedulerLoopQuarantineBranchType::kThreadLocalDefault:
return kBranchTypeThreadLocalDefaultStr;
case SchedulerLoopQuarantineBranchType::kMain:
return kBranchTypeMainStr;
case SchedulerLoopQuarantineBranchType::kAdvancedMemorySafetyChecks:
return kBranchTypeAdvancedMemorySafetyChecksStr;
}
NOTREACHED();
}
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
// JSON parsing options.
constexpr int kJSONParserOptions =
JSONParserOptions::JSON_PARSE_CHROMIUM_EXTENSIONS |
JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS;
// JSON keys for parameters.
constexpr char kKeyEnableQuarantine[] = "enable-quarantine";
constexpr char kKeyEnableZapping[] = "enable-zapping";
constexpr char kKeyLeakOnDestruction[] = "leak-on-destruction";
constexpr char kKeyBranchCapacityInBytes[] = "branch-capacity-in-bytes";
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
} // namespace
::partition_alloc::internal::SchedulerLoopQuarantineConfig
GetSchedulerLoopQuarantineConfiguration(
const std::string& process_type,
SchedulerLoopQuarantineBranchType branch_type) {
::partition_alloc::internal::SchedulerLoopQuarantineConfig config = {};
std::string_view process_type_str = process_type;
if (process_type_str.empty()) {
process_type_str = kProcessTypeBrowserStr;
}
// Should not be a special name.
DCHECK_NE(process_type_str, kProcessTypeWildcardStr);
std::string_view branch_type_str =
GetSchedulerLoopQuarantineBranchTypeStr(branch_type);
// Set a branch name like "browser/main" or "renderer/*";
std::string branch_name =
base::StrCat({process_type_str, "/", branch_type_str});
// `std::string::copy` does not append a null character.
branch_name.copy(config.branch_name, sizeof(config.branch_name) - 1);
config.branch_name[sizeof(config.branch_name) - 1] = '\0';
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
if (!FeatureList::IsEnabled(
features::kPartitionAllocSchedulerLoopQuarantine)) {
return config; // Feature disabled.
}
// TODO(https://crbug.com/434693933): Also read from command-line switches
// to support an enterprise policy. It is loaded after PA configuration in
// child processes so we should pass it from the Browser process via switches.
std::string config_str =
features::kPartitionAllocSchedulerLoopQuarantineConfig.Get();
std::optional<Value::Dict> config_processes =
JSONReader::ReadDict(config_str, kJSONParserOptions);
if (!config_processes) {
LOG(ERROR) << "Unparseable JSON: " << config_str;
return config; // Ill-formed JSON; disabled.
}
const Value::Dict* config_entry = nullptr;
const Value::Dict* config_current_process =
config_processes->FindDict(process_type_str);
if (config_current_process) {
// First, try the exact match.
config_entry = config_current_process->FindDict(branch_type_str);
// Falls back to thread-local default unless global.
if (!config_entry &&
branch_type != SchedulerLoopQuarantineBranchType::kGlobal &&
branch_type !=
SchedulerLoopQuarantineBranchType::kAdvancedMemorySafetyChecks) {
config_entry =
config_current_process->FindDict(kBranchTypeThreadLocalDefaultStr);
}
}
Value::Dict* config_wildcard_process =
config_processes->FindDict(kProcessTypeWildcardStr);
if (!config_entry && config_wildcard_process) {
// Couldn't find a configuration entry with the exact process name match.
// Look-up an entry with a process name being "*".
config_entry = config_wildcard_process->FindDict(branch_type_str);
// Falls back to thread-local default unless global.
if (!config_entry &&
branch_type != SchedulerLoopQuarantineBranchType::kGlobal) {
config_entry =
config_wildcard_process->FindDict(kBranchTypeThreadLocalDefaultStr);
}
}
if (!config_entry) {
VLOG(1) << "No entry found for " << branch_name << ".";
return config; // No config found; disabled.
}
config.enable_quarantine = config_entry->FindBool(kKeyEnableQuarantine)
.value_or(config.enable_quarantine);
config.enable_zapping =
config_entry->FindBool(kKeyEnableZapping).value_or(config.enable_zapping);
config.leak_on_destruction = config_entry->FindBool(kKeyLeakOnDestruction)
.value_or(config.leak_on_destruction);
config.branch_capacity_in_bytes =
static_cast<size_t>(config_entry->FindInt(kKeyBranchCapacityInBytes)
.value_or(config.branch_capacity_in_bytes));
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
return config;
}
} // namespace base::allocator