-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GC.AllocateUninitializedArray should tighten its logic around when to return zeroed arrays #13292
Comments
AllocateUninitializedArray is trying to ask a question: Is GC going to crash when it sees arbitrary bit pattern in this array? If the GC was able to handle object references that contain garbage, we would remove this check completely. |
I think I worded my original description poorly. This was inspired by https://github.com/dotnet/corefx/issues/40485, where that user did not expect the private array pool to be returning "garbage" data. In that case, they expected the values coming back from the pooled arrays to be constrained to a particular range. In the issue here, I'm considering the more general case of being able to assign arbitrary bit patterns to value types having bypassed the ctor and all other public APIs. Consider the Given these constraints on |
I think about ArrayPool.Shared as C++ new/delete with all its unsafety. It is not surprising to me that you get back unitialized memory with invalid values. You are not supposed to operate on the elements in these arrays before initializing them with good values, just like in C++.
It can tear on 32-bit platforms. And I think bulk copying via |
The shared pool would return junky data before (outside direct user control; due to it being shared); so shouldn't be an issue to return junkier data? i.e. you couldn't rely on it having anything sensible for your problem domain in it (a bigger constraint than not valid for the data type); without overwriting the returned array first. So reading from it without writing to it first is an invalid use. This differs from the custom pool as that can be fully under user control for a specific use (though reading without overwriting first; or always clearing on return, for a custom pool still seems overly dangerous as random results will bleed through) |
@GrabYourPitchforks are you ok closing this or is there still a concern? |
Looks like our stance is that |
Thanks for checking up on it Maoni. Much appreciated. :) |
of course! :) |
In dotnet/coreclr#24504, the
ArrayPool<T>
-derived types were changed to useGC.AllocateUninitializedArray<T>(...)
under the covers as a performance optimization. TheAllocateUninitializedArray
method usesRuntimeHelpers.IsReferenceOrContainsReferences
as an implementation detail when determining whether to return a zeroed array or an uninitialized array back to the caller.https://github.com/dotnet/coreclr/blob/af9edac7acd4735eebd7a293b27b1c509989dae8/src/System.Private.CoreLib/src/System/GC.cs#L665-L668
This logic is a little too relaxed. It appears that the
AllocateUninitializedArray
method is trying to ask the question "Is any arbitrary bit pattern valid for type T?", but theIsReferenceOrContainsReferences
method answers the separate question "Is type T 'interesting' to the GC?"There are several value types where
IsReferenceOrContainsReferences
returns false but which cannot store arbitrary bit patterns without causing problems. Among them:0
or1
)The behaviors of calling instance methods on these types or of consuming these types when they've been initialized with arbitrary bit patterns is undefined, and it could result in anything from nonsensical answers being generated to crashing the runtime.
Normally we'd tell developers not to look at the elements of arrays returned by
GC.AllocateUninitializedArray
orArrayPool<T>.Rent
, but there are some situations where they might not be able to control it. For example, if I'm under the VS debugger and I step past a call toAllocateUninitializedArray
orRent
, the debugger could start listing the array's elements automatically even though my own code wouldn't have done such a thing.If we do not want to change
GC.AllocateUninitializedArray
(perhaps because we consider it an "unsafe" API), we should at least consider putting the stronger checks into theArrayPool<T>
-derived classes.The text was updated successfully, but these errors were encountered: