| Index: base/metrics/histogram_persistence.cc
|
| diff --git a/base/metrics/histogram_persistence.cc b/base/metrics/histogram_persistence.cc
|
| index ae7d48b77f8791545bce6a5e84e7b255d405e899..535d11409276ea117357676c64061ca7c3eb3f7b 100644
|
| --- a/base/metrics/histogram_persistence.cc
|
| +++ b/base/metrics/histogram_persistence.cc
|
| @@ -76,6 +76,7 @@ struct PersistentHistogramData {
|
| uint32_t ranges_checksum;
|
| PersistentMemoryAllocator::Reference counts_ref;
|
| HistogramSamples::Metadata samples_metadata;
|
| + HistogramSamples::Metadata logged_metadata;
|
|
|
| // Space for the histogram name will be added during the actual allocation
|
| // request. This must be the last field of the structure. A zero-size array
|
| @@ -111,6 +112,22 @@ BucketRanges* CreateRangesFromData(HistogramBase::Sample* ranges_data,
|
| return ranges.release();
|
| }
|
|
|
| +// Calculate the number of bytes required to store all of a histogram's
|
| +// "counts". This will return zero (0) if |bucket_count| is not valid.
|
| +size_t CalculateRequiredCountsBytes(size_t bucket_count) {
|
| + // 2 because each "sample count" also requires a backup "logged count"
|
| + // used for calculating the delta during snapshot operations.
|
| + const unsigned kBytesPerBucket = 2 * sizeof(HistogramBase::AtomicCount);
|
| +
|
| + // If the |bucket_count| is such that it would overflow the return type,
|
| + // perhaps as the result of a malicious actor, then return zero to
|
| + // indicate the problem to the caller.
|
| + if (bucket_count > std::numeric_limits<uint32_t>::max() / kBytesPerBucket)
|
| + return 0;
|
| +
|
| + return bucket_count * kBytesPerBucket;
|
| +}
|
| +
|
| } // namespace
|
|
|
| const Feature kPersistentHistogramsFeature{
|
| @@ -244,14 +261,21 @@ HistogramBase* CreatePersistentHistogram(
|
| HistogramBase::AtomicCount* counts_data =
|
| allocator->GetAsObject<HistogramBase::AtomicCount>(
|
| histogram_data.counts_ref, kTypeIdCountsArray);
|
| - if (!counts_data ||
|
| - allocator->GetAllocSize(histogram_data.counts_ref) <
|
| - histogram_data.bucket_count * sizeof(HistogramBase::AtomicCount)) {
|
| + size_t counts_bytes =
|
| + CalculateRequiredCountsBytes(histogram_data.bucket_count);
|
| + if (!counts_data || !counts_bytes ||
|
| + allocator->GetAllocSize(histogram_data.counts_ref) < counts_bytes) {
|
| RecordCreateHistogramResult(CREATE_HISTOGRAM_INVALID_COUNTS_ARRAY);
|
| NOTREACHED();
|
| return nullptr;
|
| }
|
|
|
| + // After the main "counts" array is a second array using for storing what
|
| + // was previously logged. This is used to calculate the "delta" during
|
| + // snapshot operations.
|
| + HistogramBase::AtomicCount* logged_data =
|
| + counts_data + histogram_data.bucket_count;
|
| +
|
| std::string name(histogram_data_ptr->name);
|
| HistogramBase* histogram = nullptr;
|
| switch (histogram_data.histogram_type) {
|
| @@ -262,8 +286,10 @@ HistogramBase* CreatePersistentHistogram(
|
| histogram_data.maximum,
|
| ranges,
|
| counts_data,
|
| + logged_data,
|
| histogram_data.bucket_count,
|
| - &histogram_data_ptr->samples_metadata);
|
| + &histogram_data_ptr->samples_metadata,
|
| + &histogram_data_ptr->logged_metadata);
|
| DCHECK(histogram);
|
| break;
|
| case LINEAR_HISTOGRAM:
|
| @@ -273,8 +299,10 @@ HistogramBase* CreatePersistentHistogram(
|
| histogram_data.maximum,
|
| ranges,
|
| counts_data,
|
| + logged_data,
|
| histogram_data.bucket_count,
|
| - &histogram_data_ptr->samples_metadata);
|
| + &histogram_data_ptr->samples_metadata,
|
| + &histogram_data_ptr->logged_metadata);
|
| DCHECK(histogram);
|
| break;
|
| case BOOLEAN_HISTOGRAM:
|
| @@ -282,7 +310,9 @@ HistogramBase* CreatePersistentHistogram(
|
| name,
|
| ranges,
|
| counts_data,
|
| - &histogram_data_ptr->samples_metadata);
|
| + logged_data,
|
| + &histogram_data_ptr->samples_metadata,
|
| + &histogram_data_ptr->logged_metadata);
|
| DCHECK(histogram);
|
| break;
|
| case CUSTOM_HISTOGRAM:
|
| @@ -290,8 +320,10 @@ HistogramBase* CreatePersistentHistogram(
|
| name,
|
| ranges,
|
| counts_data,
|
| + logged_data,
|
| histogram_data.bucket_count,
|
| - &histogram_data_ptr->samples_metadata);
|
| + &histogram_data_ptr->samples_metadata,
|
| + &histogram_data_ptr->logged_metadata);
|
| DCHECK(histogram);
|
| break;
|
| default:
|
| @@ -375,16 +407,15 @@ HistogramBase* AllocatePersistentHistogram(
|
| return nullptr;
|
| }
|
|
|
| + // If CalculateRequiredCountsBytes() returns zero then the bucket_count
|
| + // was not valid.
|
| size_t bucket_count = bucket_ranges->bucket_count();
|
| - // An overflow such as this, perhaps as the result of a milicious actor,
|
| - // could lead to writing beyond the allocation boundary and into other
|
| - // memory. Just fail the allocation and let the caller deal with it.
|
| - if (bucket_count > std::numeric_limits<int32_t>::max() /
|
| - sizeof(HistogramBase::AtomicCount)) {
|
| + size_t counts_bytes = CalculateRequiredCountsBytes(bucket_count);
|
| + if (!counts_bytes) {
|
| NOTREACHED();
|
| return nullptr;
|
| }
|
| - size_t counts_bytes = bucket_count * sizeof(HistogramBase::AtomicCount);
|
| +
|
| size_t ranges_bytes = (bucket_count + 1) * sizeof(HistogramBase::Sample);
|
| PersistentMemoryAllocator::Reference ranges_ref =
|
| allocator->Allocate(ranges_bytes, kTypeIdRangesArray);
|
|
|