From 5bb96599dc35d7bfe360762423ef97c83cbd3ff5 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Tue, 17 Sep 2024 16:36:06 +0000 Subject: [PATCH] Validate performance counter data Validate performance counter data to avoid excessive looping and memory consumption. --- .../Windows/Advapi32/Interop.PERF_INFO.cs | 84 ++++ .../src/Properties/InternalsVisibleTo.cs | 6 + .../src/Resources/Strings.resx | 10 +- ...stem.Diagnostics.PerformanceCounter.csproj | 1 + .../Diagnostics/PerformanceCounterLib.cs | 76 +++- ...iagnostics.PerformanceCounter.Tests.csproj | 7 +- .../tests/ValidationTests.cs | 428 ++++++++++++++++++ .../src/Resources/Strings.resx | 62 +-- .../Diagnostics/ProcessManager.Windows.cs | 21 +- 9 files changed, 634 insertions(+), 61 deletions(-) create mode 100644 src/libraries/System.Diagnostics.PerformanceCounter/src/Properties/InternalsVisibleTo.cs create mode 100644 src/libraries/System.Diagnostics.PerformanceCounter/tests/ValidationTests.cs diff --git a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.PERF_INFO.cs b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.PERF_INFO.cs index 97d6de64b831f4..a5436fbe0f22c3 100644 --- a/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.PERF_INFO.cs +++ b/src/libraries/Common/src/Interop/Windows/Advapi32/Interop.PERF_INFO.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.InteropServices; @@ -13,6 +14,17 @@ internal static partial class Advapi32 internal struct PERF_COUNTER_BLOCK { internal int ByteLength; + + internal static readonly int SizeOf = Marshal.SizeOf(); + + public readonly void Validate(int bufferSize) + { + if (ByteLength < SizeOf || + ByteLength > bufferSize) + { + ThrowInvalidOperationException(typeof(PERF_COUNTER_BLOCK)); + } + } } [StructLayout(LayoutKind.Sequential)] @@ -28,6 +40,20 @@ internal struct PERF_COUNTER_DEFINITION internal int CounterType; internal int CounterSize; internal int CounterOffset; + + internal static readonly int SizeOf = Marshal.SizeOf(); + + public readonly void Validate(int bufferSize) + { + if (ByteLength < SizeOf || + ByteLength > bufferSize || + CounterSize < 0 || + CounterOffset < 0 || + CounterOffset > bufferSize) + { + ThrowInvalidOperationException(typeof(PERF_COUNTER_DEFINITION)); + } + } } [StructLayout(LayoutKind.Sequential)] @@ -49,6 +75,23 @@ internal struct PERF_DATA_BLOCK internal long PerfTime100nSec; internal int SystemNameLength; internal int SystemNameOffset; + + internal const int Signature1Int = (int)'P' + ('E' << 16); + internal const int Signature2Int = (int)'R' + ('F' << 16); + internal static readonly int SizeOf = Marshal.SizeOf(); + + public readonly void Validate(int bufferSize) + { + if (Signature1 != Signature1Int || + Signature2 != Signature2Int || + TotalByteLength < SizeOf || + TotalByteLength > bufferSize || + HeaderLength < SizeOf || + HeaderLength > TotalByteLength) + { + ThrowInvalidOperationException(typeof(PERF_DATA_BLOCK)); + } + } } [StructLayout(LayoutKind.Sequential)] @@ -61,9 +104,23 @@ internal struct PERF_INSTANCE_DEFINITION internal int NameOffset; internal int NameLength; + internal static readonly int SizeOf = Marshal.SizeOf(); + internal static ReadOnlySpan GetName(in PERF_INSTANCE_DEFINITION instance, ReadOnlySpan data) => (instance.NameLength == 0) ? default : MemoryMarshal.Cast(data.Slice(instance.NameOffset, instance.NameLength - sizeof(char))); // NameLength includes the null-terminator + + public readonly void Validate(int bufferSize) + { + if (ByteLength < SizeOf || + ByteLength > bufferSize || + NameOffset < 0 || + NameLength < 0 || + checked (NameOffset + NameLength) > ByteLength) + { + ThrowInvalidOperationException(typeof(PERF_INSTANCE_DEFINITION)); + } + } } [StructLayout(LayoutKind.Sequential)] @@ -83,6 +140,29 @@ internal struct PERF_OBJECT_TYPE internal int CodePage; internal long PerfTime; internal long PerfFreq; + + internal static readonly int SizeOf = Marshal.SizeOf(); + + public readonly void Validate(int bufferSize) + { + if (HeaderLength < SizeOf || + HeaderLength > TotalByteLength || + HeaderLength > DefinitionLength || + DefinitionLength < SizeOf || + DefinitionLength > TotalByteLength || + TotalByteLength > bufferSize || + NumCounters < 0 || + checked + ( + // This is a simple check, not exact, since it depends on how instances are specified. + (NumInstances <= 0 ? 0 : NumInstances * PERF_INSTANCE_DEFINITION.SizeOf) + + NumCounters * PERF_COUNTER_DEFINITION.SizeOf + ) > bufferSize + ) + { + ThrowInvalidOperationException(typeof(PERF_OBJECT_TYPE)); + } + } } [StructLayout(LayoutKind.Sequential)] @@ -106,4 +186,8 @@ public override string ToString() } } } + + [DoesNotReturn] + public static void ThrowInvalidOperationException(Type type) => + throw new InvalidOperationException(SR.Format(SR.InvalidPerfData, type.Name)); } diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/Properties/InternalsVisibleTo.cs b/src/libraries/System.Diagnostics.PerformanceCounter/src/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000000000..ca32a806401c7c --- /dev/null +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/Properties/InternalsVisibleTo.cs @@ -0,0 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("System.Diagnostics.PerformanceCounter.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001004b86c4cb78549b34bab61a3b1800e23bfeb5b3ec390074041536a7e3cbd97f5f04cf0f857155a8928eaa29ebfd11cfbbad3ba70efea7bda3226c6a8d370a4cd303f714486b6ebc225985a638471e6ef571cc92a4613c00b8fa65d61ccee0cbe5f36330c9a01f4183559f1bef24cc2917c6d913e3a541333a1d05d9bed22b38cb")] diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/src/Resources/Strings.resx b/src/libraries/System.Diagnostics.PerformanceCounter/src/Resources/Strings.resx index 829c04125b3732..187cc802b358ea 100644 --- a/src/libraries/System.Diagnostics.PerformanceCounter/src/Resources/Strings.resx +++ b/src/libraries/System.Diagnostics.PerformanceCounter/src/Resources/Strings.resx @@ -1,4 +1,5 @@ - + + + \ No newline at end of file diff --git a/src/libraries/System.Diagnostics.PerformanceCounter/tests/ValidationTests.cs b/src/libraries/System.Diagnostics.PerformanceCounter/tests/ValidationTests.cs new file mode 100644 index 00000000000000..6bc3a9614c7e76 --- /dev/null +++ b/src/libraries/System.Diagnostics.PerformanceCounter/tests/ValidationTests.cs @@ -0,0 +1,428 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using Xunit; + +using static Interop.Advapi32; + +namespace System.Diagnostics.Tests +{ + public static class ValidationTests + { + [Theory] + [MemberData(nameof(InvalidDataBlocksToTest))] + public static void ValidateDataBlock(byte[] data) + { + using (PerformanceCounter counter = GetPerformanceCounterLib(out PerformanceCounterLib lib)) + { + InvalidOperationException ex = Assert.Throws(() => GetCategorySample(lib, data)); + Assert.Contains(nameof(PERF_DATA_BLOCK), ex.Message); + } + } + + public static IEnumerable InvalidDataBlocksToTest() + { + int validSize = PERF_DATA_BLOCK.SizeOf; + + yield return Create(0, validSize); + yield return Create(1, validSize); + yield return Create(-1, validSize); + yield return Create(validSize, 0); + yield return Create(validSize, 1); + yield return Create(validSize, -1); + yield return Create(validSize, validSize + 1); + yield return Create(validSize - 1, validSize); + + static object[] Create(int totalByteLength, int headerLength) + { + PERF_DATA_BLOCK perfDataBlock = new() + { + TotalByteLength = totalByteLength, + HeaderLength = headerLength, + Signature1 = PERF_DATA_BLOCK.Signature1Int, + Signature2 = PERF_DATA_BLOCK.Signature2Int + }; + + return new object[] { StructToByteArray(perfDataBlock) }; + } + } + + [Theory] + [MemberData(nameof(InvalidObjectTypesToTest))] + public static void ValidateObjectType(byte[] data) + { + using (PerformanceCounter counter = GetPerformanceCounterLib(out PerformanceCounterLib lib)) + { + InvalidOperationException ex = Assert.Throws(() => GetCategorySample(lib, data)); + Assert.Contains(nameof(PERF_OBJECT_TYPE), ex.Message); + } + } + + public static IEnumerable InvalidObjectTypesToTest() + { + VerifyInitialized(); + + int validSize = PERF_OBJECT_TYPE.SizeOf; + yield return new object[] { Create(0, validSize, validSize) }; + yield return new object[] { Create(1, validSize, validSize) }; + yield return new object[] { Create(-1, validSize, validSize) }; + yield return new object[] { Create(validSize, 0, validSize) }; + yield return new object[] { Create(validSize, 1, validSize) }; + yield return new object[] { Create(validSize, -1, validSize) }; + yield return new object[] { Create(validSize, validSize, 0) }; + yield return new object[] { Create(validSize, validSize, 1) }; + yield return new object[] { Create(validSize, validSize, -1) }; + yield return new object[] { Create(validSize - 1, validSize, validSize) }; + yield return new object[] { Create(validSize, validSize - 1, validSize) }; + yield return new object[] { Create(validSize, validSize + 1, validSize) }; + yield return new object[] { Create(validSize, validSize, validSize - 1) }; + yield return new object[] { Create(validSize, validSize, validSize + 1) }; + + static byte[] Create(int totalByteLength, int headerLength, int definitionLength) + { + PERF_DATA_BLOCK perfDataBlock = CreatePerfDataBlock(); + perfDataBlock.TotalByteLength = PERF_DATA_BLOCK.SizeOf + PERF_OBJECT_TYPE.SizeOf; + + PERF_OBJECT_TYPE perfObjectType = new() + { + TotalByteLength = totalByteLength, + HeaderLength = headerLength, + DefinitionLength = definitionLength, + ObjectNameTitleIndex = s_ObjectNameTitleIndex + }; + + return StructsToByteArray(perfDataBlock, perfObjectType); + } + } + + [Theory] + [MemberData(nameof(ObjectTypeWithHighCountsToTest))] + public static void ValidateObjectTypeWithHighCounts(byte[] data) + { + using (PerformanceCounter counter = GetPerformanceCounterLib(out PerformanceCounterLib lib)) + { + Exception ex = Assert.ThrowsAny(() => GetCategorySample(lib, data)); + Assert.True(ex is InvalidOperationException || ex is OverflowException, $"Type:{ex.GetType().Name}."); + } + } + + public static IEnumerable ObjectTypeWithHighCountsToTest() + { + VerifyInitialized(); + + yield return new object[] { Create(Array.MaxLength, 1) }; + yield return new object[] { Create(1, -1) }; // numInstances with -1 is supported, but numCounters is not. + yield return new object[] { Create(1, Array.MaxLength) }; + yield return new object[] { Create(Array.MaxLength / 1000, 1) }; + yield return new object[] { Create(1, Array.MaxLength / 1000) }; + yield return new object[] { Create(Array.MaxLength, Array.MaxLength) }; + + static byte[] Create(int numInstances, int numCounters) + { + PERF_DATA_BLOCK perfDataBlock = CreatePerfDataBlock(); + PERF_OBJECT_TYPE perfObjectType = CreatePerfObjectType(numInstances, numCounters); + + // Add a single instance definition. + PERF_COUNTER_DEFINITION perfCounterDefinition = CreatePerfCounterDefinition(); + + return StructsToByteArray(perfDataBlock, perfObjectType, perfCounterDefinition); + } + } + + [Theory] + [MemberData(nameof(InvalidCounterDefinitionsToTest))] + public static void ValidateCounterDefinition(byte[] data) + { + using (PerformanceCounter counter = GetPerformanceCounterLib(out PerformanceCounterLib lib)) + { + InvalidOperationException ex = Assert.Throws(() => GetCategorySample(lib, data)); + Assert.Contains(nameof(PERF_COUNTER_DEFINITION), ex.Message); + } + } + + public static IEnumerable InvalidCounterDefinitionsToTest() + { + VerifyInitialized(); + + int validSize = PERF_INSTANCE_DEFINITION.SizeOf; + + yield return new object[] { Create(0, 4) }; + yield return new object[] { Create(1, 4) }; + yield return new object[] { Create(-1, 4) }; + yield return new object[] { Create(validSize, -1) }; + yield return new object[] { Create(validSize - 1, 4) }; + yield return new object[] { Create(validSize, 1000) }; + + static byte[] Create(int byteLength, int counterOffset) + { + PERF_DATA_BLOCK perfDataBlock = CreatePerfDataBlock(); + PERF_OBJECT_TYPE perfObjectType = CreatePerfObjectType(numInstances: 0, numCounters: 1); + PERF_COUNTER_DEFINITION perfCounterDefinition = new() + { + ByteLength = byteLength, + CounterOffset = counterOffset, + CounterSize = 4, + CounterNameTitleIndex = s_ObjectNameTitleIndex + }; + + return StructsToByteArray(perfDataBlock, perfObjectType, perfCounterDefinition); + } + } + + [Theory] + [MemberData(nameof(InvalidInstancesToTest))] + public static void ValidateInstanceDefinition(byte[] data) + { + using (PerformanceCounter counter = GetPerformanceCounterLib(out PerformanceCounterLib lib)) + { + InvalidOperationException ex = Assert.Throws(() => GetCategorySample(lib, data)); + Assert.Contains(nameof(PERF_INSTANCE_DEFINITION), ex.Message); + } + } + + public static IEnumerable InvalidInstancesToTest() + { + VerifyInitialized(); + + int validSize = PERF_INSTANCE_DEFINITION.SizeOf; + + yield return new object[] { Create(0, 0, 0) }; + yield return new object[] { Create(1, 0, 0) }; + yield return new object[] { Create(-1, 0, 0) }; + yield return new object[] { Create(validSize, -1, 0) }; + yield return new object[] { Create(validSize, 0, -1) }; + yield return new object[] { Create(validSize, 1000, 0) }; + yield return new object[] { Create(validSize, 0, 1000) }; + + static byte[] Create(int byteLength, int nameOffset, int nameLength) + { + PERF_DATA_BLOCK perfDataBlock = CreatePerfDataBlock(); + PERF_OBJECT_TYPE perfObjectType = CreatePerfObjectType(numInstances: 1, numCounters: 1); + PERF_COUNTER_DEFINITION perfCounterDefinition = CreatePerfCounterDefinition(); + PERF_INSTANCE_DEFINITION perfInstanceDefinition = new() + { + ByteLength = byteLength, + NameOffset = nameOffset, + NameLength = nameLength, + }; + + return StructsToByteArray(perfDataBlock, perfObjectType, perfCounterDefinition, perfInstanceDefinition); + } + } + + [Theory] + [MemberData(nameof(InvalidCounterBlocksToTest))] + public static void ValidateCounterBlock(byte[] data) + { + using (PerformanceCounter counter = GetPerformanceCounterLib(out PerformanceCounterLib lib)) + { + InvalidOperationException ex = Assert.Throws(() => GetCategorySample(lib, data)); + Assert.Contains(nameof(PERF_COUNTER_BLOCK), ex.Message); + } + } + + public static IEnumerable InvalidCounterBlocksToTest() + { + VerifyInitialized(); + + yield return new object[] { Create(-1) }; + yield return new object[] { Create(0) }; + yield return new object[] { Create(1) }; + + static byte[] Create(int byteLength) + { + PERF_DATA_BLOCK perfDataBlock = CreatePerfDataBlock(); + PERF_OBJECT_TYPE perfObjectType = CreatePerfObjectType(numInstances: 1, numCounters: 1); + PERF_COUNTER_DEFINITION perfCounterDefinition = CreatePerfCounterDefinition(); + PERF_INSTANCE_DEFINITION perfInstanceDefinition = CreatePerfInstanceDefinition(); + PERF_COUNTER_BLOCK perfCounterBlock = new() + { + ByteLength = byteLength + }; + + int value = 0; + + return StructsToByteArray(perfDataBlock, perfObjectType, perfCounterDefinition, perfInstanceDefinition, perfCounterBlock, value); + } + } + + private static byte[] StructToByteArray(T value) where T : struct + { + int size = Marshal.SizeOf(value); + byte[] arr = new byte[size]; + CopyStruct(value, arr, 0, size); + return arr; + } + + private static byte[] StructsToByteArray(T1 value1, T2 value2) + where T1 : struct where T2 : struct + { + int size1 = Marshal.SizeOf(value1); + int size2 = Marshal.SizeOf(value2); + byte[] arr = new byte[size1 + size2]; + CopyStruct(value1, arr, 0, size1); + CopyStruct(value2, arr, size1, size2); + return arr; + } + + private static byte[] StructsToByteArray(T1 value1, T2 value2, T3 value3) + where T1 : struct where T2 : struct where T3 : struct + { + int size1 = Marshal.SizeOf(value1); + int size2 = Marshal.SizeOf(value2); + int size3 = Marshal.SizeOf(value3); + byte[] arr = new byte[size1 + size2 + size3]; + CopyStruct(value1, arr, 0, size1); + CopyStruct(value2, arr, size1, size2); + CopyStruct(value3, arr, size1 + size2, size3); + return arr; + } + + private static byte[] StructsToByteArray(T1 value1, T2 value2, T3 value3, T4 value4) + where T1 : struct where T2 : struct where T3 : struct where T4 : struct + { + int size1 = Marshal.SizeOf(value1); + int size2 = Marshal.SizeOf(value2); + int size3 = Marshal.SizeOf(value3); + int size4 = Marshal.SizeOf(value4); + byte[] arr = new byte[size1 + size2 + size3 + size4]; + CopyStruct(value1, arr, 0, size1); + CopyStruct(value2, arr, size1, size2); + CopyStruct(value3, arr, size1 + size2, size3); + CopyStruct(value4, arr, size1 + size2 + size3, size4); + return arr; + } + + private static byte[] StructsToByteArray(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) + where T1 : struct where T2 : struct where T3 : struct where T4 : struct where T5 : struct where T6 : struct + { + int size1 = Marshal.SizeOf(value1); + int size2 = Marshal.SizeOf(value2); + int size3 = Marshal.SizeOf(value3); + int size4 = Marshal.SizeOf(value4); + int size5 = Marshal.SizeOf(value5); + int size6 = Marshal.SizeOf(value5); + byte[] arr = new byte[size1 + size2 + size3 + size4 + size5 + size6]; + CopyStruct(value1, arr, 0, size1); + CopyStruct(value2, arr, size1, size2); + CopyStruct(value3, arr, size1 + size2, size3); + CopyStruct(value4, arr, size1 + size2 + size3, size4); + CopyStruct(value5, arr, size1 + size2 + size3 + size4, size5); + CopyStruct(value6, arr, size1 + size2 + size3 + size4 + size5, size6); + return arr; + } + + private static void CopyStruct(T value, byte[] data, int startIndex, int length) where T : struct + { + int size = Marshal.SizeOf(value); + IntPtr ptr = Marshal.AllocHGlobal(size); + + try + { + Marshal.StructureToPtr(value, ptr, true); + Marshal.Copy(ptr, data, startIndex, length); + } + finally + { + Marshal.FreeHGlobal(ptr); + } + } + + private static PerformanceCounter GetPerformanceCounterLib(out PerformanceCounterLib lib) + { + PerformanceCounter counterSample = Helpers.RetryOnAllPlatformsWithClosingResources(() => + new PerformanceCounter("Processor", "Interrupts/sec", "0", ".")); + + counterSample.BeginInit(); + Assert.NotNull(counterSample); + + FieldInfo fi = typeof(PerformanceCounterLib).GetField("s_libraryTable", BindingFlags.Static | BindingFlags.NonPublic); + Hashtable libs = (Hashtable)fi.GetValue(null); + CategoryEntry category = default; + + bool found = false; + lib = default; + foreach (string key in libs.Keys) + { + lib = (PerformanceCounterLib)libs[key]; + Assert.NotNull(lib); + + category = (CategoryEntry)lib.CategoryTable["Processor"]; + if (category != null) + { + found = true; + break; + } + } + + Assert.True(found); + + s_ObjectNameTitleIndex = category.NameIndex; + + return counterSample; + } + + private static CategorySample GetCategorySample(PerformanceCounterLib lib, byte[] data) + { + CategoryEntry entry = (CategoryEntry)lib.CategoryTable["Processor"]; + return new CategorySample(data, entry, lib); + } + + private static int s_ObjectNameTitleIndex { get; set; } = -1; + + private static void VerifyInitialized() + { + if (s_ObjectNameTitleIndex == -1) + { + using (PerformanceCounter counter = GetPerformanceCounterLib(out PerformanceCounterLib _)) { } + } + + Assert.True(s_ObjectNameTitleIndex != -1); + } + + private static PERF_DATA_BLOCK CreatePerfDataBlock() => + new PERF_DATA_BLOCK + { + TotalByteLength = PERF_DATA_BLOCK.SizeOf + PERF_OBJECT_TYPE.SizeOf + PERF_COUNTER_DEFINITION.SizeOf, + HeaderLength = PERF_DATA_BLOCK.SizeOf, + Signature1 = PERF_DATA_BLOCK.Signature1Int, + Signature2 = PERF_DATA_BLOCK.Signature2Int, + NumObjectTypes = 1 + }; + + private static PERF_OBJECT_TYPE CreatePerfObjectType(int numInstances, int numCounters) => + new PERF_OBJECT_TYPE + { + TotalByteLength = PERF_OBJECT_TYPE.SizeOf + PERF_COUNTER_DEFINITION.SizeOf, + HeaderLength = PERF_OBJECT_TYPE.SizeOf, + DefinitionLength = PERF_OBJECT_TYPE.SizeOf + PERF_COUNTER_DEFINITION.SizeOf, + ObjectNameTitleIndex = s_ObjectNameTitleIndex, + NumCounters = numCounters, + NumInstances = numInstances + }; + + private static PERF_COUNTER_DEFINITION CreatePerfCounterDefinition() => + new PERF_COUNTER_DEFINITION + { + ByteLength = PERF_COUNTER_DEFINITION.SizeOf, + CounterOffset = 4, + CounterSize = 4, + CounterNameTitleIndex = s_ObjectNameTitleIndex + }; + + private static PERF_INSTANCE_DEFINITION CreatePerfInstanceDefinition() => + new() + { + ByteLength = PERF_INSTANCE_DEFINITION.SizeOf, + NameOffset = 0, + NameLength = 0, + + // Setting this calls GetInstanceNamesFromIndex() which will validate the real (valid) definition. + ParentObjectTitleIndex = s_ObjectNameTitleIndex + }; + } +} diff --git a/src/libraries/System.Diagnostics.Process/src/Resources/Strings.resx b/src/libraries/System.Diagnostics.Process/src/Resources/Strings.resx index d6ac4b8688cca3..6a2f970d6aec81 100644 --- a/src/libraries/System.Diagnostics.Process/src/Resources/Strings.resx +++ b/src/libraries/System.Diagnostics.Process/src/Resources/Strings.resx @@ -1,16 +1,17 @@ - - @@ -332,4 +333,7 @@ sysctl {0} failed with {1} error. - + + Invalid performance counter data with type '{0}'. + + \ No newline at end of file diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs index 5762874bbef77f..86399634e4da73 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Windows.cs @@ -476,18 +476,25 @@ private static ProcessInfo[] GetProcessInfos(PerformanceCounterLib library, int List threadInfos = new List(); ref readonly PERF_DATA_BLOCK dataBlock = ref MemoryMarshal.AsRef(data); + dataBlock.Validate(data.Length); int typePos = dataBlock.HeaderLength; + ReadOnlySpan dataSpan; + for (int i = 0; i < dataBlock.NumObjectTypes; i++) { - ref readonly PERF_OBJECT_TYPE type = ref MemoryMarshal.AsRef(data.Slice(typePos)); + dataSpan = data.Slice(typePos); + ref readonly PERF_OBJECT_TYPE type = ref MemoryMarshal.AsRef(dataSpan); + type.Validate(dataSpan.Length); PERF_COUNTER_DEFINITION[] counters = new PERF_COUNTER_DEFINITION[type.NumCounters]; int counterPos = typePos + type.HeaderLength; for (int j = 0; j < type.NumCounters; j++) { - ref readonly PERF_COUNTER_DEFINITION counter = ref MemoryMarshal.AsRef(data.Slice(counterPos)); + dataSpan = data.Slice(counterPos); + ref readonly PERF_COUNTER_DEFINITION counter = ref MemoryMarshal.AsRef(dataSpan); + counter.Validate(dataSpan.Length); string counterName = library.GetCounterName(counter.CounterNameTitleIndex); @@ -503,7 +510,9 @@ private static ProcessInfo[] GetProcessInfos(PerformanceCounterLib library, int int instancePos = typePos + type.DefinitionLength; for (int j = 0; j < type.NumInstances; j++) { - ref readonly PERF_INSTANCE_DEFINITION instance = ref MemoryMarshal.AsRef(data.Slice(instancePos)); + dataSpan = data.Slice(instancePos); + ref readonly PERF_INSTANCE_DEFINITION instance = ref MemoryMarshal.AsRef(dataSpan); + instance.Validate(dataSpan.Length); ReadOnlySpan instanceName = PERF_INSTANCE_DEFINITION.GetName(in instance, data.Slice(instancePos)); @@ -563,7 +572,11 @@ private static ProcessInfo[] GetProcessInfos(PerformanceCounterLib library, int instancePos += instance.ByteLength; - instancePos += MemoryMarshal.AsRef(data.Slice(instancePos)).ByteLength; + dataSpan = data.Slice(instancePos); + ref readonly PERF_COUNTER_BLOCK perfCounterBlock = ref MemoryMarshal.AsRef(dataSpan); + perfCounterBlock.Validate(dataSpan.Length); + + instancePos += perfCounterBlock.ByteLength; } typePos += type.TotalByteLength;