Skip to content

Instantly share code, notes, and snippets.

@DavidKlempfner
Last active December 27, 2020 10:03
Show Gist options
  • Save DavidKlempfner/d3eb11b9e947cef46bff3c1e2bfd23d6 to your computer and use it in GitHub Desktop.
Save DavidKlempfner/d3eb11b9e947cef46bff3c1e2bfd23d6 to your computer and use it in GitHub Desktop.
HackingForeach.cs
using System;
using System.Collections.Generic;
using System.Reflection;
namespace ConsoleApp9
{
class Program
{
static void Main(string[] args)
{
var list = new List<int> { 2, 3 };
int version = GetVersion(list); //2
bool isFirstIteration = true;
foreach (var item in list)
{
//The first time this foreach loop is
//executed, list.GetEnumerator() will be called.
//It will return an instance of Enumerator
//with its version field set to list._version
//which is 2, and which doesn't change.
if (isFirstIteration)
{
UpdateListUIntMaxValueTimes(list);
version = GetVersion(list); //1
isFirstIteration = false;
}
//Usually the following line would cause a run time error at the start of the next iteration:
list[1] = 5;
//But because of the call to UpdateListUIntMaxValueTimes(list),
//the above line causes list._version to be 2,
//which is the same as Enumerator.version.
//So the foreach loop thinks the list has not been updated.
//On the second iteration, list._version = 3
//which != what Enumerator.version has which is 2.
//On the 3rd iteration when MoveNext() is called,
//it will see that:
//list._version != Enumerator.version
//and an InvalidOperationException will be thrown.
version = GetVersion(list);
}
}
/// <summary>
/// This will modify the list 4294967295 times
/// which will overflow list._version
/// and make it one less than what it was
/// </summary>
/// <param name="list"></param>
static void UpdateListUIntMaxValueTimes(List<int> list)
{
for (uint i = 0; i < uint.MaxValue; i++)
{
list[0] = list[0];
}
}
private static int GetVersion(List<int> list)
{
int version = Convert.ToInt32(GetInstanceField(typeof(List<int>), list, "_version"));
return version;
}
private static object GetInstanceField(Type type, object instance, string fieldName)
{
BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
FieldInfo field = type.GetField(fieldName, bindFlags);
return field.GetValue(instance);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment