-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
InlineArray
size validation does not handle all cases
#95193
Comments
cc @VSadov |
Why is that InlineArray limitation there in the first place? |
The limit has been chosen arbitrarily: #83776 (comment). @VSadov, please let me know if you decide to change the limit so that we can make the necessary adjustments in the Mono runtime. |
This is likely just a bug with size check not covering one of scenarios or not checking for overflows. Possibly just on one runtime. The idea is that once an inline array is too large to be safely used on stack, the heap-only use does not have enough advantage over regular arrays. Thus the limit is 1MiB. More than that is likely a mistake or a misuse of the feature. |
But does the C# compiler know about this limit? |
I am not sure C# compiler knows sizes of structs in general. If that is the case it can`t enforce limits. |
Then how can we consider it "mistake or misuse" if the compiler is using these automatically as an implementation detail but can't and doesn't check whether it'll trip over the undocumented limit? |
There is notably significant advantage in certain types of perf oriented code. Removing that indirection can be very important and can allow developers to codify their own powerful types that have large amounts of inline data. For example, it could be used to build specialized types of ECS. The current limit is in fact arbitrary, and there are platforms where it might overflow sooner. There are likewise platforms where it might overflow later. This is no different than I think its ultimately better to just not restrict it, outside of disallowing overflow, and to allow users to freely use it as they desire. If there is concern over "too large" types, then having an analyzer that warns for users specifying over a certain size seems like a reasonable middleground. |
We have number of similar internal runtime implementation limits. These limits vary between runtime flavors and architectures. For the specific internal uses of InlineArray by the compiler, I would expect that the (artificially complicated) code is much more likely to hit other internal runtime implementation limits than the current InlineArray limit. |
I don't understand what the limit is there to help with, though. What happens if it's removed? The reasoning cites differences like stack sizes showing up, but that's the case even with the limit in place.
The generated IL is artificially complicated? Then let's simplify it. |
Allowing sizes to go all the value to MaxValue is a corner case bug farm. I would be ok with making the limit higher, but I do not think we should remove it. For example, I would be ok with making the limit same as the field offset limit.
I meant artificially complicated user code. |
What is the current limit here? Is this (the InlineArray limit) something that would be worth exposing a property for? We expose |
I do not think it is possible to have no limit. There are limits on max field offsets (effectively size of structs), stack size limits. Some of that would depend on platform/runtime/64bit. Would it be better to just let the user randomly running into one of these? With ordinary structs running into limits is rare, since you'd have to define every field, and you need a lot of fields. |
At least for stack size, you can already run into it, with or without the current IA limit. |
|
This is no different than the 1MB currently selected.
This is no different than things like These limits exist, but they are designed to be high enough that almost no code will ever need to consider them. But even with these very high limits, people still do hit them and then we have to decide if its worth changing or adjusting it later (and in some cases we can't change it later).
While There are clearly cases for users to have inline buffers that large in other types and while they may primarily use things like It's been discussed in other threads how having the ability to define something like Another example is that its common to do instancing in games/graphics and so you may have Users can likewise define structs that are bigger than It really seems better to just not give this a real limit (outside preventing overflow) and to let users hit it themselves as they would if they did // This is fine today
[StructLayout(LayoutKind.Sequential, Size = 1024 * 1024 * 256)]
struct S1 { }
// This is fine today
struct S2 { public int x; public S1 s; }
// This fails today
struct S3 { public int x; public S1 s; public int y; }
// This fails today
class C { public S2 s; }
|
I think we need to get this to some conclusion. |
Can someone explain why I recently used this in an app to get a framebuffer of 640x480 pixels into the .bss segment of the executable (on native AOT). It hit the 1 MB limit because on native AOT it got enforced even for sequential layout (that's a separate bug I have a PR for). I replaced it with a fixed array because that one doesn't have a made up limit. |
I still don't think it should be banned and called out many of the same scenarios you did above. I strongly believe the ecosystem is better off making this similar to other similar cases and not arbitrarily limiting this to something we think is "reasonable" (especially something as small as 1MB). Allowing this be up towards |
Yep, my question was for @VSadov and @jkotas who are asking for a limit. You and I are on the same page of not understanding why is this different from other cases that have been possible for 20 years. Adding a limit now is a breaking change because we shipped this way. Not "breaking 20 years of written code"-level, but breaking an LTS release nonetheless. The fact we enforce it on Auto is kind of meaningless because that's not the popular layout. |
I have shared my opinion in #95193 (comment). If you would like to allow the full range to
The type loader has silent integer overflows and it is allowing types that the runtime is not able to represent correctly (e.g. the type at the top of the issue). It is bug that we need to fix. It is not ok to have silent integer overflows in the type loader that lead to incorrect behavior. Fixing this issue can break some of the existing code that is trying to push the limits. We can file a breaking change notice for this fix if we consider it to be impactful enough. These silent integer overflows are present in handling of
prints |
If you consider unrestricted StructLayout Size equally problematic and we'd fix both at the same time, I can get on board with that. I think the limit will have to be high enough that we don't feel bad if we break someone whose code worked for 20 years. I would put it wherever we have implementation limitations, not document the specific number, and don't try to keep it in sync with Mono. |
After thinking about this for a while I think making the limit the same as
Even though |
(Mono in .NET 8 throws a TLE on the sample above; no fix needed there) |
Although Mono doesn't like a using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
unsafe
{
Console.WriteLine (sizeof (BigElement)); // this works in CoreCLR even if the size of BigElement is greater than 1 << 27
Console.WriteLine (sizeof(/*BigArray*/ BigStruct)); // this throws on CoreCLR if small_element is past MaxFieldOffset
}
struct BigStruct
{
BigElement element;
byte small_element;
}
[StructLayout(LayoutKind.Sequential, Size = MaxFieldOffset.Value)]
struct BigElement
{
}
static class MaxFieldOffset {
public const int Value = 1 << 20 + 1; /* 1 << 20 works in Mono; upto ((1<<27) - 1) - 6 works with CoreCLR */
} |
Description
The size of an
InlineArray
is supposed to be limited to 1MiB, but the example below slips through:Reproduction Steps
Given the type definitions from earlier:
Expected behavior
The runtime should throw a
TypeLoadException
.Actual behavior
No exception occurs, and
sizeof(BigArray)
returns-2
.Regression?
No response
Known Workarounds
No response
Configuration
No response
Other information
No response
The text was updated successfully, but these errors were encountered: