-
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
Codegen for a constant-sized stackalloc into a Span<T> should be on par with explicit-sized no-init local declaration #52979
Comments
No stack cookie is not a good stuff for unsafe stack buffer manipulation like this. We should really be emitting the stack cookie for situations like this, see https:/dotnet/designs/blob/main/accepted/2021/runtime-security-mitigations.md#stack-buffer-overflow-protection |
I'm not sure I understand what you mean by "unsafe stack manipulation", could you elaborate on that? The scenario here would be to only interact with the buffer through a |
I agree that it is more reasonable to omit stack cookies for 100% safe Span-based code, but it is not easy for the JIT to tell from the IL whether it is the case. |
#52065 is related to this. |
I think there will always be conflicts between "performance" and "safety" and I doubt the JIT will be able to "always" do the right thing. Even native compilers provide switches and pragmas to turn these features on/off (often defaulting to "on") and so perhaps we should support suppressing these features in .NET as well. I'm also not really a fan of calling the stack cookie a "security mitigation" (as its referred to in the doc linked above) as it really just blocks certain kinds of buffer overflow (most often accidental). If you happen to skip the cookie (either on purpose or on accident) then it effectively does nothing. Things like address sanitization, shadow stacks, or the CPU hardware features are often more effective modern alternatives for places where security is actually important. |
What would you call it instead? Mitigations are about reducing harmful effects, not reliably eliminating them. I have included the definition of the "mitigation" at the top of the doc since that is common misunderstanding. |
I don't have a suggestion for a better name, I've just always viewed the stack cookie as more of a bounds checking/correctness/safety feature than a security one. |
Agree that shadow stacks are likely a more effective mitigation going forward. But they're still not widely supported among processors our users are likely to use, and stack canaries have a history of catching these types of buffer overruns before they can lead to critical issues. We really should be performing stack checks (canaries, shadow stacks, whatever tech) more frequently. But at the same time, it totally makes sense to investigate whether the JIT can detect certain patterns as safe and elide the check. I don't know enough about the JIT to say how much work that might entail, though. :( |
CC @AndyAyersMS. |
Figured I'd mention that if #32060 makes it (hopefully), this will also be allowed: struct Space
{
public byte Buffer[128];
}
[SkipLocalsInit]
static void M2()
{
Space space;
Foo(space.Buffer); // Buffer has an implicit Span<byte> conversion
} This would presumably result in the same exact codegen as the Additionally: if #32060 makes it, similarly it'll be possible to create a |
All stack buffers should get stack cookie by default, irrespective of the exact pattern used to create them. We may be missing it for some patterns today. As @GrabYourPitchforks said in #52979 (comment), we really should be performing stack checks more frequently. If the JIT is able to prove that all code operating on the buffer is safe with proper bounds checks, it should be ok to omit the stack cookie. I am not sure how often would this optimization help in practice. |
Description
I believe the codegen for "safe constant stackallocs" could be improved, to match what can already be manually achieved by just declaring a local with
[SkipLocalsInit]
, of a custom struct type of an explicit size. What I mean by "safe constant stackalloc":[StructLayout(Size = ...)]
)Span<T>
(Span<byte>
?)Here's the current codegen when using such a declaration in a method:
C# code (click to expand):
asm x86-64 code (click to expand):
And here's the codegen by using that trick with a local struct type with explicit layout:
C# code (click to expand):
asm x86-64 code (click to expand):
No stack cookie, no stack guard page check, no branches, no pushed registers - just all the good stuff 😄
It should be doable to do this under these two conditions, given that
Span<T>
already protects against buffer overruns (so no stack cookie is fine), and most importantly given that we can already get this same codegen, just with extra steps though?Configuration
The .NET 6 builds with Disasmo are with a local checked build from ~2 weeks ago.
Regression?
Nope.
category:cq
theme:stack-allocation
The text was updated successfully, but these errors were encountered: