Prevent concurrent use corruption from causing infinite loops (#16991)
authorBen Adams <thundercat@illyriad.co.uk>
Sat, 17 Mar 2018 01:24:16 +0000 (21:24 -0400)
committerJan Kotas <jkotas@microsoft.com>
Sat, 17 Mar 2018 01:24:16 +0000 (18:24 -0700)
src/mscorlib/Resources/Strings.resx
src/mscorlib/shared/System/Collections/Generic/Dictionary.cs
src/mscorlib/src/System/ThrowHelper.cs

index 28346b2..5d37b50 100644 (file)
   <data name="InvalidOperation_CollectionCorrupted" xml:space="preserve">
     <value>A prior operation on this collection was interrupted by an exception. Collection's state is no longer trusted.</value>
   </data>
+  <data name="InvalidOperation_ConcurrentOperationsNotSupported" xml:space="preserve">
+    <value>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.</value>
+  </data>
   <data name="InvalidOperation_ConstructorNotAllowedOnInterface" xml:space="preserve">
     <value>Interface cannot have constructors.</value>
   </data>
index 77d3055..16b7cdc 100644 (file)
@@ -364,6 +364,7 @@ namespace System.Collections.Generic
             int i = -1;
             int[] buckets = _buckets;
             Entry[] entries = _entries;
+            int collisionCount = 0;
             if (buckets != null)
             {
                 IEqualityComparer<TKey> 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);
 
index 4eb92be..408b439 100644 (file)
@@ -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,
     }
 }