I've written a source file with 1000 classes all inheriting from the one above:
class Program
{
static void Main(string[] args)
{
Class700 class700 = new Class700();
}
}
class Class1 { public Class1() { } }
class Class2 : Class1 { public Class2() { } }
class Class3 : Class2 { public Class3() { } }
class Class4 : Class3 { public Class4() { } }
class Class5 : Class4 { public Class5() { } }
//class ClassN : ClassN-1 { public ClassN() { } } where N = 2 to N = 1000
I get a StackOverflow exception on Class700
, however this changes every time I run it, but usually it's around 700.
Can anyone explain why at approximately level 700, a StackOverflow occurs, and why this changes every time I run the program?
I'm using Windows 8.1 Enterprise 64 bit.
Seeing it bomb at 700 is hard to explain, but we are surely not looking at the real code. You'd only get something like this in an auto-generated code case, of course nobody would ever write something like this by hand.
But yes, SOE is certainly possible with code like this. Invisible to the eye, but the constructor of a derived class always calls the constructor of its base class. If you don't write it yourself then the compiler will auto-generate that call. All the way down to the System.Object constructor.
How much stack space is required for the constructor is something you can see with the debugger. Just isolate the code for two classes, create a Class2 object and set breakpoints on the Class2 and the Class1 constructors. You want Debug + Windows + Registers, write down the value of the ESP register, the stack pointer, when the breakpoints hit. RSP in 64-bit mode.
Doing the same with your code snippet, I get 0x024C012C and 0x024C00E4, a difference of 72 bytes. Extrapolating that to 700 classes, that would requires 700 x 72 = 50400 bytes. Not close to SOE, your program bombs when it consumes one megabyte in 32-bit code, 4 megabytes when compiled with a target platform forced to x64. The jitter has overhead as well, a number you cannot guess at until you subtract the difference.
You can increase the size of the stack with Editbin.exe, /STACK option. Or create a Thread, use the constructor that lets you set the stack size.
And yes, that it doesn't repeat well is normal. The CLR implements several anti-malware techniques, one of them is starting the stack at a random location. That makes it very difficult for malware to exploit .NET code with a buffer overflow attack. Which is a quite unlikely threat to .NET code in general, not a lot of code around that uses stackalloc
, but the counter-measure is very cheap to implement.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With