blob: debb73d4a0ac80ce6e4b7a15790490dce927571d [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/vrp_flags/vrp_flags_impl.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/process/process.h"
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
#include "components/vrp_flags/vrp_flags.h"
#include "sandbox/policy/switches.h"
namespace vrp_flags {
namespace {
std::string GetProcessType() {
std::string type =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII("type");
if (type.empty()) {
return "browser";
}
return type;
}
} // namespace
// static
VrpFlagsImpl* VrpFlagsImpl::GetInstance() {
static base::NoDestructor<VrpFlagsImpl> instance;
return instance.get();
}
VrpFlagsImpl::VrpFlagsImpl() = default;
VrpFlagsImpl::~VrpFlagsImpl() = default;
void VrpFlagsImpl::Bind(mojo::PendingReceiver<mojom::VrpFlags> receiver) {
receivers_.Add(this, std::move(receiver));
}
void VrpFlagsImpl::GetWriteLocations(GetWriteLocationsCallback callback) {
EnsureAllocated();
std::vector<uint64_t> write_locations;
for (uint64_t* loc : write_locations_) {
// SAFETY - Cast addresses and pass to renderer as locations to attempt
// a write. While this is technically a leak of privileged process addresses
// it is deliberate and only reachable in --vrp-flags mode.
UNSAFE_BUFFERS(write_locations.push_back(reinterpret_cast<uint64_t>(loc)););
}
std::move(callback).Run(write_locations, arbitrary_value_);
}
void VrpFlagsImpl::WriteAttempted(uint64_t location,
WriteAttemptedCallback callback) {
CHECK(!write_allocations_.empty());
bool success = false;
uint32_t index = 0;
for (uint64_t* loc : write_locations_) {
if (UNSAFE_BUFFERS(loc == reinterpret_cast<uint64_t*>(location))) {
if (*loc == arbitrary_value_) {
LOG(ERROR) << "Security VrpFlags Write Success index=" << index
<< " location=" << location << " value=" << arbitrary_value_;
success = true;
}
break;
}
index++;
}
if (!for_testing_) {
LOG(FATAL) << "Security VrpFlags Write Attempted in " << GetProcessType()
<< " process! location=" << location << " success=" << success;
}
std::move(callback).Run(success);
}
void VrpFlagsImpl::GetReadPrefix(GetReadPrefixCallback callback) {
EnsureAllocated();
std::move(callback).Run(read_flag_->prefix);
}
void VrpFlagsImpl::ReadAttempted(const base::UnguessableToken& flag,
ReadAttemptedCallback callback) {
CHECK(read_flag_);
bool success = (read_flag_ && read_flag_->flag == flag);
if (!for_testing_) {
LOG(FATAL) << "Security VrpFlags Read Attempted in " << GetProcessType()
<< " process! success=" << success;
}
std::move(callback).Run(success);
}
void VrpFlagsImpl::SetReadValueForTesting(const base::UnguessableToken& flag) {
EnsureAllocated();
read_flag_->flag = flag;
}
void VrpFlagsImpl::EnsureAllocated() {
if (!write_allocations_.empty()) {
return;
}
read_flag_ = std::make_unique<ReadFlag>();
read_flag_->prefix = base::UnguessableToken::Create();
arbitrary_value_ = base::RandUint64();
const size_t kSizes[] = {4096, 16384, 65536, 262144, 1048576};
for (size_t size : kSizes) {
auto allocation =
base::HeapArray<uint64_t>::WithSize(size / sizeof(uint64_t));
// Pick a random offset that is not at the start.
size_t offset = base::RandInt(1, (size / sizeof(uint64_t)) - 1);
// SAFETY - Keep the pointer for later, the allocation outlives uses
// of the pointer, and this is only reachable in --vrp-flags mode.
UNSAFE_BUFFERS(uint64_t* address = &allocation.data()[offset]);
write_locations_.push_back(address);
write_allocations_.push_back(std::move(allocation));
}
}
} // namespace vrp_flags