-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Remove a few instances of unsafe code #116111
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
Conversation
src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Memory/src/System/Buffers/SequenceReaderExtensions.Binary.cs
Outdated
Show resolved
Hide resolved
|
||
return (int)(hash1 + (hash2 * 1566083941)); | ||
} | ||
return GetNonRandomizedHashCode(new ReadOnlySpan<char>(ref _firstChar, _stringLength)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this negatively impact perf?
The implementation as it existed was benefiting from strings being null terminated. The span-based overload can't rely on that and pays a tad for it.
See large comment above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the same time the Span one special cased 1,2,3 length, it was an improvement last time I ran benchmark, I'll do again
This comment was marked as resolved.
This comment was marked as resolved.
@@ -39,16 +39,15 @@ private static unsafe bool TryReadMultisegment<T>(ref SequenceReader<byte> reade | |||
Debug.Assert(reader.UnreadSpan.Length < sizeof(T)); | |||
|
|||
// Not enough data in the current segment, try to peek for the data we need. | |||
T buffer = default; | |||
Span<byte> tempSpan = new Span<byte>(&buffer, sizeof(T)); | |||
Span<byte> tempSpan = stackalloc byte[sizeof(T)]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a regression?
SharpLab
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am sure whoever wrote the initial code was trying to avoid the stack probe/GS cookie with this workaround. I think this sets a bad example and we should never accept code like this anymore, but does it mean we're going to leave all the existing places as is?
To be honest, at this point I suspect we're going to focus on unsafe only in places nobody use on the hot path because so far in most places regressions are not avoidable like the one with null terminator, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a reason why a stack probe is any more necessary in M1 than in M2, or in M3 in SharpLab. The JIT should be able to avoid them with const sized stackallocs into spans, just as it does for locals then wrapped in spans.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a reason why a stack probe is any more necessary in M1 than in M2, or in M3
We've discussed that in #52979
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. ... 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.
I am not sure we can build any model in JIT like that, cc @dotnet/jit-contrib
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're serious about that, then we should fix the JIT to emit the cookie in all cases, including like M3. If M1 needs a cookie because, as quoted in that issue, someone may the use unsafe code to manipulate the span, then M2 and M3 do as well, and really any code where a ref is taken to a local and passed around. Otherwise, also quoting from that issue:
I agree that it is more reasonable to omit stack cookies for 100% safe Span-based code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've reverted the change since there is nothing we can do
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Current thinking is to re-examine the stack protections in the JIT for .NET 11.
Aside from the concerns above, we are placing increasing amounts of state on the stack that traditionally only lived in the heap.
src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: Stephen Toub <stoub@microsoft.com>
@stephentoub anything else here? |
@@ -178,6 +178,10 @@ private void ComputeHash(IncrementalHash sha256, string path, DateTime lastChang | |||
sha256.AppendData(_byteBuffer, 0, length); | |||
sha256.AppendData(Separator, 0, Separator.Length); | |||
|
|||
#if NET |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you could change this for all builds to be:
BinaryPrimitives.WriteInt64LittleEndian(_byteBuffer, lastChangedUtc.Ticks);
The endianness shouldn't matter here, as it's just being used in an in-memory hash to determine whether something changes or not.
Could probably also use:
long ticks = lastChangedUtc.Ticks;
MemoryMarshal.Write(_byteBuffer, ref ticks);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could probably also use:
I was avoiding this one because it is a candidate for "requires unsafe" due to its generic nature
{ | ||
Write(new ReadOnlySpan<byte>(&value, 1)); | ||
Write([value]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this new up an array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, Write accepts a ReadOnlySpan. It emits the equivalent of var tmp = value; Write(new ReadOnlySpan<byte>(in tmp));
.
No description provided.