From f2f327d8c5bdfe0d9c93bb70d5c3e7cd162351ab Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 16 Mar 2018 21:24:16 -0400 Subject: [PATCH] Prevent concurrent use corruption from causing infinite loops (#16991) --- src/mscorlib/Resources/Strings.resx | 3 +++ .../System/Collections/Generic/Dictionary.cs | 27 ++++++++++++++++++++++ src/mscorlib/src/System/ThrowHelper.cs | 6 +++++ 3 files changed, 36 insertions(+) diff --git a/src/mscorlib/Resources/Strings.resx b/src/mscorlib/Resources/Strings.resx index 28346b2..5d37b50 100644 --- a/src/mscorlib/Resources/Strings.resx +++ b/src/mscorlib/Resources/Strings.resx @@ -2539,6 +2539,9 @@ A prior operation on this collection was interrupted by an exception. Collection's state is no longer trusted. + + Operations that change non-concurrent collections must have exclusive access. A concurrent update was performed on this collection and corrupted its state. The collection's state is no longer correct. + Interface cannot have constructors. diff --git a/src/mscorlib/shared/System/Collections/Generic/Dictionary.cs b/src/mscorlib/shared/System/Collections/Generic/Dictionary.cs index 77d3055..16b7cdc 100644 --- a/src/mscorlib/shared/System/Collections/Generic/Dictionary.cs +++ b/src/mscorlib/shared/System/Collections/Generic/Dictionary.cs @@ -364,6 +364,7 @@ namespace System.Collections.Generic int i = -1; int[] buckets = _buckets; Entry[] entries = _entries; + int collisionCount = 0; if (buckets != null) { IEqualityComparer comparer = _comparer; @@ -382,6 +383,13 @@ namespace System.Collections.Generic } i = entries[i].next; + if (collisionCount >= entries.Length) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + } + collisionCount++; } while (true); } else @@ -400,6 +408,13 @@ namespace System.Collections.Generic } i = entries[i].next; + if (collisionCount >= entries.Length) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + } + collisionCount++; } while (true); } } @@ -469,6 +484,12 @@ namespace System.Collections.Generic } i = entries[i].next; + if (collisionCount >= entries.Length) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + } collisionCount++; } while (true); } @@ -501,6 +522,12 @@ namespace System.Collections.Generic } i = entries[i].next; + if (collisionCount >= entries.Length) + { + // The chain of entries forms a loop; which means a concurrent update has happened. + // Break out of the loop and throw, rather than looping forever. + ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported(); + } collisionCount++; } while (true); diff --git a/src/mscorlib/src/System/ThrowHelper.cs b/src/mscorlib/src/System/ThrowHelper.cs index 4eb92be..408b439 100644 --- a/src/mscorlib/src/System/ThrowHelper.cs +++ b/src/mscorlib/src/System/ThrowHelper.cs @@ -284,6 +284,11 @@ namespace System throw GetInvalidOperationException(ExceptionResource.InvalidOperation_NoValue); } + internal static void ThrowInvalidOperationException_ConcurrentOperationsNotSupported() + { + throw GetInvalidOperationException(ExceptionResource.InvalidOperation_ConcurrentOperationsNotSupported); + } + internal static void ThrowArraySegmentCtorValidationFailedExceptions(Array array, int offset, int count) { throw GetArraySegmentCtorValidationFailedException(array, offset, count); @@ -591,6 +596,7 @@ namespace System AsyncMethodBuilder_InstanceNotInitialized, ArgumentNull_SafeHandle, NotSupported_StringComparison, + InvalidOperation_ConcurrentOperationsNotSupported, } } -- 2.7.4