diff --git a/src/coreclr/System.Private.CoreLib/src/System/ArgIterator.cs b/src/coreclr/System.Private.CoreLib/src/System/ArgIterator.cs index e7c2a99eeefa2..d49d1dcb270b5 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/ArgIterator.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/ArgIterator.cs @@ -24,7 +24,7 @@ private struct SigPointer private int _remainingArgs; // # of remaining args. #if TARGET_WINDOWS // Native Varargs are not supported on Unix - // ArgIterator is a ref struct. It does not require pinning. + // ArgIterator is a ref struct. It does not require pinning, therefore Unsafe.AsPointer is safe. // This method null checks the this pointer as a side-effect. private ArgIterator* ThisPtr => (ArgIterator*)Unsafe.AsPointer(ref _argCookie); diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs index 590fd5b18cee0..697788316ba03 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs @@ -865,7 +865,9 @@ public static unsafe IReadOnlyDictionary GetConfigurationVariabl Configurations = new Dictionary() }; - _EnumerateConfigurationValues(Unsafe.AsPointer(ref context), &ConfigCallback); +#pragma warning disable CS8500 // takes address of managed type + _EnumerateConfigurationValues(&context, &ConfigCallback); +#pragma warning restore CS8500 return context.Configurations!; } diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs index d47cae0a18ed0..16b1659caeab7 100644 --- a/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs @@ -206,11 +206,12 @@ private static unsafe object[] InitializeStatics(IntPtr gcStaticRegionStart, int nint blockAddr = MethodTable.SupportsRelativePointers ? (nint)ReadRelPtr32(pBlock) : *pBlock; if ((blockAddr & GCStaticRegionConstants.Uninitialized) == GCStaticRegionConstants.Uninitialized) { +#pragma warning disable CS8500 // takes address of managed type object? obj = null; RuntimeImports.RhAllocateNewObject( new IntPtr(blockAddr & ~GCStaticRegionConstants.Mask), (uint)GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP, - Unsafe.AsPointer(ref obj)); + &obj); if (obj == null) { RuntimeExceptionHelpers.FailFast("Failed allocating GC static bases"); @@ -232,7 +233,8 @@ private static unsafe object[] InitializeStatics(IntPtr gcStaticRegionStart, int Unsafe.Add(ref rawSpineData, currentBase) = obj; // Update the base pointer to point to the pinned object - *pBlock = *(IntPtr*)Unsafe.AsPointer(ref obj); + *pBlock = *(IntPtr*)&obj; +#pragma warning restore CS8500 } currentBase++; diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs index 62ed9a722671a..0ee16609157ab 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs @@ -695,7 +695,9 @@ public static unsafe IReadOnlyDictionary GetConfigurationVariabl Configurations = new Dictionary() }; - RuntimeImports.RhEnumerateConfigurationValues(Unsafe.AsPointer(ref context), &ConfigCallback); +#pragma warning disable CS8500 // takes address of managed type + RuntimeImports.RhEnumerateConfigurationValues(&context, &ConfigCallback); +#pragma warning restore CS8500 return context.Configurations!; } @@ -830,7 +832,9 @@ static T[] AllocateNewUninitializedArray(int length, bool pinned) throw new OverflowException(); T[]? array = null; - RuntimeImports.RhAllocateNewArray(MethodTable.Of(), (uint)length, (uint)flags, Unsafe.AsPointer(ref array)); +#pragma warning disable CS8500 // takes address of managed type + RuntimeImports.RhAllocateNewArray(MethodTable.Of(), (uint)length, (uint)flags, &array); +#pragma warning restore CS8500 if (array == null) throw new OutOfMemoryException(); @@ -857,7 +861,9 @@ public static unsafe T[] AllocateArray(int length, bool pinned = false) throw new OverflowException(); T[]? array = null; - RuntimeImports.RhAllocateNewArray(MethodTable.Of(), (uint)length, (uint)flags, Unsafe.AsPointer(ref array)); +#pragma warning disable CS8500 // takes address of managed type + RuntimeImports.RhAllocateNewArray(MethodTable.Of(), (uint)length, (uint)flags, &array); +#pragma warning restore CS8500 if (array == null) throw new OutOfMemoryException(); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 5d10b4159feca..fdaae7fdb715b 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -350,9 +350,11 @@ private CompilationResult CompileMethodInternal(IMethodNode methodCodeNodeNeedin IntPtr exception; IntPtr nativeEntry; uint codeSize; +#pragma warning disable CS8500 // takes address of managed type var result = JitCompileMethod(out exception, - _jit, (IntPtr)Unsafe.AsPointer(ref _this), _unmanagedCallbacks, + _jit, (IntPtr)(&_this), _unmanagedCallbacks, ref methodInfo, (uint)CorJitFlag.CORJIT_FLAG_CALL_GETJITFLAGS, out nativeEntry, out codeSize); +#pragma warning restore CS8500 if (exception != IntPtr.Zero) { if (_lastException != null) diff --git a/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostRuntimeContract.cs b/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostRuntimeContract.cs index a75f34be6942c..99ebe3adfe5d0 100644 --- a/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostRuntimeContract.cs +++ b/src/installer/tests/Assets/Projects/HostApiInvokerApp/HostRuntimeContract.cs @@ -46,8 +46,9 @@ private static void Test_get_runtime_property(string[] args) static string GetProperty(string name, host_runtime_contract contract) { - Span nameSpan = stackalloc byte[Encoding.UTF8.GetMaxByteCount(name.Length)]; - byte* namePtr = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(nameSpan)); + int nameSize = Encoding.UTF8.GetMaxByteCount(name.Length); + byte* namePtr = stackalloc byte[nameSize]; + Span nameSpan = new Span(namePtr, nameSize); int nameLen = Encoding.UTF8.GetBytes(name, nameSpan); nameSpan[nameLen] = 0; @@ -86,8 +87,9 @@ public static void Test_bundle_probe(string[] args) unsafe static void Probe(host_runtime_contract contract, string path) { - Span pathSpan = stackalloc byte[Encoding.UTF8.GetMaxByteCount(path.Length)]; - byte* pathPtr = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(pathSpan)); + int pathSize = Encoding.UTF8.GetMaxByteCount(path.Length); + byte* pathPtr = stackalloc byte[pathSize]; + Span pathSpan = new Span(pathPtr, pathSize); int pathLen = Encoding.UTF8.GetBytes(path, pathSpan); pathSpan[pathLen] = 0; diff --git a/src/libraries/Common/src/System/Number.NumberBuffer.cs b/src/libraries/Common/src/System/Number.NumberBuffer.cs index 5b4fc7a7564e8..1fb6949182ac9 100644 --- a/src/libraries/Common/src/System/Number.NumberBuffer.cs +++ b/src/libraries/Common/src/System/Number.NumberBuffer.cs @@ -29,7 +29,9 @@ internal unsafe ref struct NumberBuffer public bool IsNegative; public bool HasNonZeroTail; public NumberBufferKind Kind; - public Span Digits; + public byte* DigitsPtr; + public int DigitsLength; + public readonly Span Digits => new Span(DigitsPtr, DigitsLength); public NumberBuffer(NumberBufferKind kind, byte* digits, int digitsLength) : this(kind, new Span(digits, digitsLength)) { @@ -48,7 +50,8 @@ public NumberBuffer(NumberBufferKind kind, Span digits) IsNegative = false; HasNonZeroTail = false; Kind = kind; - Digits = digits; + DigitsPtr = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(digits)); // Safe since memory must be fixed + DigitsLength = digits.Length; #if DEBUG Digits.Fill(0xCC); #endif @@ -83,13 +86,6 @@ public void CheckConsistency() } #pragma warning restore CA1822 - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte* GetDigitsPointer() - { - // This is safe to do since we are a ref struct - return (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(Digits)); - } - // // Code coverage note: This only exists so that Number displays nicely in the VS watch window. So yes, I know it works. // diff --git a/src/libraries/System.Memory/tests/ArrayBufferWriter/ArrayBufferWriterTests.T.cs b/src/libraries/System.Memory/tests/ArrayBufferWriter/ArrayBufferWriterTests.T.cs index 0cf99cbab5e4d..f609e38dae344 100644 --- a/src/libraries/System.Memory/tests/ArrayBufferWriter/ArrayBufferWriterTests.T.cs +++ b/src/libraries/System.Memory/tests/ArrayBufferWriter/ArrayBufferWriterTests.T.cs @@ -424,14 +424,7 @@ public void MultipleCallsToGetSpan() Assert.True(span.Length >= 256); Span newSpan = output.GetSpan(); Assert.Equal(span.Length, newSpan.Length); - - unsafe - { - void* pSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)); - void* pNewSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(newSpan)); - Assert.Equal((IntPtr)pSpan, (IntPtr)pNewSpan); - } - + Assert.Equal(0, Unsafe.ByteOffset(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(newSpan))); Assert.Equal(span.Length, output.GetSpan().Length); } finally diff --git a/src/libraries/System.Memory/tests/MemoryMarshal/CreateFromPinnedArray.cs b/src/libraries/System.Memory/tests/MemoryMarshal/CreateFromPinnedArray.cs index 3610ea0dbfbf6..e56a24e5eea28 100644 --- a/src/libraries/System.Memory/tests/MemoryMarshal/CreateFromPinnedArray.cs +++ b/src/libraries/System.Memory/tests/MemoryMarshal/CreateFromPinnedArray.cs @@ -190,6 +190,7 @@ public static unsafe void CreateFromPinnedArrayVerifyPinning() int[] pinnedArray = { 90, 91, 92, 93, 94, 95, 96, 97, 98 }; GCHandle pinnedGCHandle = GCHandle.Alloc(pinnedArray, GCHandleType.Pinned); + // Unsafe.AsPointer is used to ensure we catch if the GC moves the memory Memory pinnedMemory = MemoryMarshal.CreateFromPinnedArray(pinnedArray, 0, 2); void* pinnedPtr = Unsafe.AsPointer(ref MemoryMarshal.GetReference(pinnedMemory.Span)); void* memoryHandlePinnedPtr = pinnedMemory.Pin().Pointer; @@ -197,8 +198,8 @@ public static unsafe void CreateFromPinnedArrayVerifyPinning() GC.Collect(); GC.Collect(2); - Assert.Equal((int)pinnedPtr, (int)Unsafe.AsPointer(ref MemoryMarshal.GetReference(pinnedMemory.Span))); - Assert.Equal((int)memoryHandlePinnedPtr, (int)pinnedGCHandle.AddrOfPinnedObject().ToPointer()); + Assert.Equal((IntPtr)pinnedPtr, (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetReference(pinnedMemory.Span))); + Assert.Equal((IntPtr)memoryHandlePinnedPtr, pinnedGCHandle.AddrOfPinnedObject()); pinnedGCHandle.Free(); } diff --git a/src/libraries/System.Memory/tests/MemoryMarshal/GetArrayDataReference.cs b/src/libraries/System.Memory/tests/MemoryMarshal/GetArrayDataReference.cs index 0fc55c64c4451..c3478421c358c 100644 --- a/src/libraries/System.Memory/tests/MemoryMarshal/GetArrayDataReference.cs +++ b/src/libraries/System.Memory/tests/MemoryMarshal/GetArrayDataReference.cs @@ -43,7 +43,7 @@ public static unsafe void GetArrayDataReference_EmptyInput_ReturnsRefToWhereFirs ref int theRef = ref MemoryMarshal.GetArrayDataReference(theArray); - Assert.True(Unsafe.AsPointer(ref theRef) != null); + Assert.False(Unsafe.IsNullRef(ref theRef)); Assert.True(Unsafe.AreSame(ref theRef, ref MemoryMarshal.GetReference(theArray.AsSpan()))); ref int theMdArrayRef = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference((Array)theArray)); // szarray passed to generalized Array helper diff --git a/src/libraries/System.Memory/tests/MemoryPool/MemoryPool.cs b/src/libraries/System.Memory/tests/MemoryPool/MemoryPool.cs index e040a363493bf..4ee663dc1dd88 100644 --- a/src/libraries/System.Memory/tests/MemoryPool/MemoryPool.cs +++ b/src/libraries/System.Memory/tests/MemoryPool/MemoryPool.cs @@ -52,6 +52,7 @@ public static void MemoryPoolSpan() { unsafe { + // Unsafe.AsPointer is safe here since it's pinned void* pSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(sp)); Assert.Equal((IntPtr)newMemoryHandle.Pointer, (IntPtr)pSpan); } @@ -77,6 +78,7 @@ public static void MemoryPoolPin(int elementIndex) { unsafe { + // Unsafe.AsPointer is safe here since it's pinned void* pSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(sp.Slice(elementIndex))); Assert.Equal((IntPtr)pSpan, ((IntPtr)newMemoryHandle.Pointer)); } @@ -112,6 +114,7 @@ public static void MemoryPoolPinOffsetAtEnd() { unsafe { + // Unsafe.AsPointer is safe here since it's pinned void* pSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(sp.Slice(elementIndex))); Assert.Equal((IntPtr)pSpan, ((IntPtr)newMemoryHandle.Pointer)); } @@ -219,11 +222,7 @@ public static void MemoryPoolTryGetArray() unsafe { Assert.True(MemoryMarshal.TryGetArray(memory, out arraySegment)); - fixed (int* pArray = arraySegment.Array) - { - void* pSpan = Unsafe.AsPointer(ref MemoryMarshal.GetReference(memory.Span)); - Assert.Equal((IntPtr)pSpan, (IntPtr)pArray); - } + Assert.Equal(0, Unsafe.ByteOffset(ref MemoryMarshal.GetArrayDataReference(arraySegment.Array), ref MemoryMarshal.GetReference(memory.Span))); } } } diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/AsSpan.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/AsSpan.cs index d6070d47c3ba9..83387f22a2d20 100644 --- a/src/libraries/System.Memory/tests/ReadOnlySpan/AsSpan.cs +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/AsSpan.cs @@ -91,6 +91,7 @@ static unsafe void Validate(string text, int start, int length, ReadOnlySpan(this Span span) Assert.True(span.IsEmpty); // Validate that empty Span is not normalized to null - Assert.True(Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)) != null); + Assert.False(Unsafe.IsNullRef(ref MemoryMarshal.GetReference(span))); } public delegate void AssertThrowsAction(Span span); @@ -98,7 +98,7 @@ public static unsafe void ValidateNonNullEmpty(this ReadOnlySpan span) Assert.True(span.IsEmpty); // Validate that empty Span is not normalized to null - Assert.True(Unsafe.AsPointer(ref MemoryMarshal.GetReference(span)) != null); + Assert.False(Unsafe.IsNullRef(ref MemoryMarshal.GetReference(span))); } public delegate void AssertThrowsActionReadOnly(ReadOnlySpan span); diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs index 7a292e019fdda..78b745e232fa1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs @@ -1120,6 +1120,14 @@ protected unsafe void WriteEvent(int eventId, long arg1, byte[]? arg2) } } + // Returns the object as a IntPtr - safe when only used for logging + internal static unsafe nint ObjectIDForEvents(object? o) + { +#pragma warning disable CS8500 // takes address of managed type + return *(nint*)&o; +#pragma warning restore CS8500 + } + #pragma warning restore 1591 /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.Threading.NativeSinks.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.Threading.NativeSinks.cs index 5ba348edd7fe2..432540c79ff15 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.Threading.NativeSinks.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.Threading.NativeSinks.cs @@ -94,7 +94,7 @@ private void ContentionLockCreated(nint LockID, nint AssociatedObjectID, ushort [NonEvent] [MethodImpl(MethodImplOptions.NoInlining)] - public void ContentionLockCreated(Lock lockObj) => ContentionLockCreated(lockObj.LockIdForEvents, lockObj.ObjectIdForEvents); + public void ContentionLockCreated(Lock lockObj) => ContentionLockCreated(lockObj.LockIdForEvents, ObjectIDForEvents(lockObj)); [Event(81, Level = EventLevel.Informational, Message = Messages.ContentionStart, Task = Tasks.Contention, Opcode = EventOpcode.Start, Version = 2, Keywords = Keywords.ContentionKeyword)] private void ContentionStart( @@ -115,7 +115,7 @@ public void ContentionStart(Lock lockObj) => ContentionFlagsMap.Managed, DefaultClrInstanceId, lockObj.LockIdForEvents, - lockObj.ObjectIdForEvents, + ObjectIDForEvents(lockObj), lockObj.OwningThreadId); [Event(91, Level = EventLevel.Informational, Message = Messages.ContentionStop, Task = Tasks.Contention, Opcode = EventOpcode.Stop, Version = 1, Keywords = Keywords.ContentionKeyword)] @@ -360,7 +360,7 @@ private void WaitHandleWaitStart( public unsafe void WaitHandleWaitStart( WaitHandleWaitSourceMap waitSource = WaitHandleWaitSourceMap.Unknown, object? associatedObject = null) => - WaitHandleWaitStart(waitSource, *(nint*)Unsafe.AsPointer(ref associatedObject)); + WaitHandleWaitStart(waitSource, ObjectIDForEvents(associatedObject)); [Event(302, Level = EventLevel.Verbose, Message = Messages.WaitHandleWaitStop, Task = Tasks.WaitHandleWait, Opcode = EventOpcode.Stop, Version = 0, Keywords = Keywords.WaitHandleKeyword)] public void WaitHandleWaitStop(ushort ClrInstanceID = DefaultClrInstanceId) diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.Threading.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.Threading.cs index 11c4dfcfe6c53..8c85770e1f0aa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.Threading.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/NativeRuntimeEventSource.Threading.cs @@ -107,7 +107,7 @@ private unsafe void ContentionLockCreated(nint LockID, nint AssociatedObjectID, [NonEvent] [MethodImpl(MethodImplOptions.NoInlining)] - public void ContentionLockCreated(Lock lockObj) => ContentionLockCreated(lockObj.LockIdForEvents, lockObj.ObjectIdForEvents); + public void ContentionLockCreated(Lock lockObj) => ContentionLockCreated(lockObj.LockIdForEvents, ObjectIDForEvents(lockObj)); [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", Justification = "Parameters to this method are primitive and are trimmer safe")] [Event(81, Level = EventLevel.Informational, Message = Messages.ContentionStart, Task = Tasks.Contention, Opcode = EventOpcode.Start, Version = 2, Keywords = Keywords.ContentionKeyword)] @@ -146,7 +146,7 @@ public void ContentionStart(Lock lockObj) => ContentionFlagsMap.Managed, DefaultClrInstanceId, lockObj.LockIdForEvents, - lockObj.ObjectIdForEvents, + ObjectIDForEvents(lockObj), lockObj.OwningThreadId); [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", Justification = "Parameters to this method are primitive and are trimmer safe")] @@ -557,7 +557,7 @@ private unsafe void WaitHandleWaitStart( public unsafe void WaitHandleWaitStart( WaitHandleWaitSourceMap waitSource = WaitHandleWaitSourceMap.Unknown, object? associatedObject = null) => - WaitHandleWaitStart(waitSource, *(nint*)Unsafe.AsPointer(ref associatedObject)); + WaitHandleWaitStart(waitSource, ObjectIDForEvents(associatedObject)); [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:UnrecognizedReflectionPattern", Justification = "Parameters to this method are primitive and are trimmer safe")] [Event(302, Level = EventLevel.Verbose, Message = Messages.WaitHandleWaitStop, Task = Tasks.WaitHandleWait, Opcode = EventOpcode.Stop, Version = 0, Keywords = Keywords.WaitHandleKeyword)] diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Icu.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Icu.cs index 4d3314f22ec59..26919ba0d50a8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Icu.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Icu.cs @@ -135,14 +135,16 @@ private static unsafe bool GetCalendarInfo(string localeName, CalendarId calenda out calendarString); } - private static bool EnumDatePatterns(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[]? datePatterns) + private static unsafe bool EnumDatePatterns(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[]? datePatterns) { datePatterns = null; IcuEnumCalendarsData callbackContext = default; callbackContext.Results = new List(); callbackContext.DisallowDuplicates = true; - bool result = EnumCalendarInfo(localeName, calendarId, dataType, ref callbackContext); +#pragma warning disable CS8500 // takes address of managed type + bool result = EnumCalendarInfo(localeName, calendarId, dataType, &callbackContext); +#pragma warning restore CS8500 if (result) { List datePatternsList = callbackContext.Results; @@ -362,13 +364,15 @@ private static int CountOccurrences(string input, char value, ref int index) return index - startIndex; } - private static bool EnumMonthNames(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[]? monthNames, ref string? leapHebrewMonthName) + private static unsafe bool EnumMonthNames(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[]? monthNames, ref string? leapHebrewMonthName) { monthNames = null; IcuEnumCalendarsData callbackContext = default; callbackContext.Results = new List(); - bool result = EnumCalendarInfo(localeName, calendarId, dataType, ref callbackContext); +#pragma warning disable CS8500 // takes address of managed type + bool result = EnumCalendarInfo(localeName, calendarId, dataType, &callbackContext); +#pragma warning restore CS8500 if (result) { // the month-name arrays are expected to have 13 elements. If ICU only returns 12, add an @@ -410,13 +414,15 @@ private static bool EnumEraNames(string localeName, CalendarId calendarId, Calen return result; } - internal static bool EnumCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[]? calendarData) + internal static unsafe bool EnumCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, out string[]? calendarData) { calendarData = null; IcuEnumCalendarsData callbackContext = default; callbackContext.Results = new List(); - bool result = EnumCalendarInfo(localeName, calendarId, dataType, ref callbackContext); +#pragma warning disable CS8500 // takes address of managed type + bool result = EnumCalendarInfo(localeName, calendarId, dataType, &callbackContext); +#pragma warning restore CS8500 if (result) { calendarData = callbackContext.Results.ToArray(); @@ -425,10 +431,12 @@ internal static bool EnumCalendarInfo(string localeName, CalendarId calendarId, return result; } - private static unsafe bool EnumCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, ref IcuEnumCalendarsData callbackContext) +#pragma warning disable CS8500 // takes address of managed type + private static unsafe bool EnumCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType dataType, IcuEnumCalendarsData* callbackContext) { - return Interop.Globalization.EnumCalendarInfo(&EnumCalendarInfoCallback, localeName, calendarId, dataType, (IntPtr)Unsafe.AsPointer(ref callbackContext)); + return Interop.Globalization.EnumCalendarInfo(&EnumCalendarInfoCallback, localeName, calendarId, dataType, (IntPtr)callbackContext); } +#pragma warning restore CS8500 [UnmanagedCallersOnly] private static unsafe void EnumCalendarInfoCallback(char* calendarStringPtr, IntPtr context) diff --git a/src/libraries/System.Private.CoreLib/src/System/Memory.cs b/src/libraries/System.Private.CoreLib/src/System/Memory.cs index 25e9778d66b53..989cac29c57c1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Memory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Memory.cs @@ -398,6 +398,7 @@ public unsafe MemoryHandle Pin() { if (typeof(T) == typeof(char) && tmpObject is string s) { + // Unsafe.AsPointer is safe since the handle pins it GCHandle handle = GCHandle.Alloc(tmpObject, GCHandleType.Pinned); ref char stringData = ref Unsafe.Add(ref s.GetRawStringData(), _index); return new MemoryHandle(Unsafe.AsPointer(ref stringData), handle); @@ -410,11 +411,13 @@ public unsafe MemoryHandle Pin() // Array is already pre-pinned if (_index < 0) { + // Unsafe.AsPointer is safe since it's pinned void* pointer = Unsafe.Add(Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(tmpObject))), _index & ReadOnlyMemory.RemoveFlagsBitMask); return new MemoryHandle(pointer); } else { + // Unsafe.AsPointer is safe since the handle pins it GCHandle handle = GCHandle.Alloc(tmpObject, GCHandleType.Pinned); void* pointer = Unsafe.Add(Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(tmpObject))), _index); return new MemoryHandle(pointer, handle); diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs index c888403677734..9dba42def297f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs @@ -407,7 +407,7 @@ public static unsafe bool TryFormatDecimal(decimal value, ReadOnlySpan= 0) { *dst++ = *p++; @@ -1627,7 +1627,7 @@ private static unsafe void Int32ToNumber(int value, ref NumberBuffer number) value = -value; } - byte* buffer = number.GetDigitsPointer(); + byte* buffer = number.DigitsPtr; byte* p = UInt32ToDecChars(buffer + Int32Precision, (uint)value, 0); int i = (int)(buffer + Int32Precision - p); @@ -1635,7 +1635,7 @@ private static unsafe void Int32ToNumber(int value, ref NumberBuffer number) number.DigitsCount = i; number.Scale = i; - byte* dst = number.GetDigitsPointer(); + byte* dst = number.DigitsPtr; while (--i >= 0) { *dst++ = *p++; @@ -1824,7 +1824,7 @@ private static unsafe void UInt32ToNumber(uint value, ref NumberBuffer number) number.DigitsCount = UInt32Precision; number.IsNegative = false; - byte* buffer = number.GetDigitsPointer(); + byte* buffer = number.DigitsPtr; byte* p = UInt32ToDecChars(buffer + UInt32Precision, value, 0); int i = (int)(buffer + UInt32Precision - p); @@ -1832,7 +1832,7 @@ private static unsafe void UInt32ToNumber(uint value, ref NumberBuffer number) number.DigitsCount = i; number.Scale = i; - byte* dst = number.GetDigitsPointer(); + byte* dst = number.DigitsPtr; while (--i >= 0) { *dst++ = *p++; @@ -2058,7 +2058,7 @@ private static unsafe void Int64ToNumber(long value, ref NumberBuffer number) value = -value; } - byte* buffer = number.GetDigitsPointer(); + byte* buffer = number.DigitsPtr; byte* p = UInt64ToDecChars(buffer + Int64Precision, (ulong)value, 0); int i = (int)(buffer + Int64Precision - p); @@ -2066,7 +2066,7 @@ private static unsafe void Int64ToNumber(long value, ref NumberBuffer number) number.DigitsCount = i; number.Scale = i; - byte* dst = number.GetDigitsPointer(); + byte* dst = number.DigitsPtr; while (--i >= 0) { *dst++ = *p++; @@ -2289,7 +2289,7 @@ private static unsafe void UInt64ToNumber(ulong value, ref NumberBuffer number) number.DigitsCount = UInt64Precision; number.IsNegative = false; - byte* buffer = number.GetDigitsPointer(); + byte* buffer = number.DigitsPtr; byte* p = UInt64ToDecChars(buffer + UInt64Precision, value, 0); int i = (int)(buffer + UInt64Precision - p); @@ -2297,7 +2297,7 @@ private static unsafe void UInt64ToNumber(ulong value, ref NumberBuffer number) number.DigitsCount = i; number.Scale = i; - byte* dst = number.GetDigitsPointer(); + byte* dst = number.DigitsPtr; while (--i >= 0) { *dst++ = *p++; @@ -2484,7 +2484,7 @@ private static unsafe void Int128ToNumber(Int128 value, ref NumberBuffer number) value = -value; } - byte* buffer = number.GetDigitsPointer(); + byte* buffer = number.DigitsPtr; byte* p = UInt128ToDecChars(buffer + Int128Precision, (UInt128)value, 0); int i = (int)(buffer + Int128Precision - p); @@ -2492,7 +2492,7 @@ private static unsafe void Int128ToNumber(Int128 value, ref NumberBuffer number) number.DigitsCount = i; number.Scale = i; - byte* dst = number.GetDigitsPointer(); + byte* dst = number.DigitsPtr; while (--i >= 0) { *dst++ = *p++; @@ -2701,7 +2701,7 @@ private static unsafe void UInt128ToNumber(UInt128 value, ref NumberBuffer numbe number.DigitsCount = UInt128Precision; number.IsNegative = false; - byte* buffer = number.GetDigitsPointer(); + byte* buffer = number.DigitsPtr; byte* p = UInt128ToDecChars(buffer + UInt128Precision, value, 0); int i = (int)(buffer + UInt128Precision - p); @@ -2709,7 +2709,7 @@ private static unsafe void UInt128ToNumber(UInt128 value, ref NumberBuffer numbe number.DigitsCount = i; number.Scale = i; - byte* dst = number.GetDigitsPointer(); + byte* dst = number.DigitsPtr; while (--i >= 0) { *dst++ = *p++; @@ -3050,7 +3050,7 @@ internal static unsafe void NumberToStringFormat(ref ValueListBuilder( Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); int digPos = number.Scale; - byte* dig = number.GetDigitsPointer(); + byte* dig = number.DigitsPtr; if (digPos > 0) { @@ -3657,7 +3657,7 @@ private static unsafe void FormatScientific(ref ValueListBuilder v { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); - byte* dig = number.GetDigitsPointer(); + byte* dig = number.DigitsPtr; vlb.Append(TChar.CastFrom((*dig != 0) ? (char)(*dig++) : '0')); @@ -3716,7 +3716,7 @@ private static unsafe void FormatGeneral(ref ValueListBuilder vlb, } } - byte* dig = number.GetDigitsPointer(); + byte* dig = number.DigitsPtr; if (digPos > 0) { @@ -3786,7 +3786,7 @@ private static void FormatPercent(ref ValueListBuilder vlb, ref Nu internal static unsafe void RoundNumber(ref NumberBuffer number, int pos, bool isCorrectlyRounded) { - byte* dig = number.GetDigitsPointer(); + byte* dig = number.DigitsPtr; int i = 0; while (i < pos && dig[i] != '\0') diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs b/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs index 8043171f596df..303ae2f43c922 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.NumberToFloatingPointBits.cs @@ -700,7 +700,7 @@ private static void AccumulateDecimalDigitsIntoBigInteger(scoped ref NumberBuffe { BigInteger.SetZero(out result); - byte* src = number.GetDigitsPointer() + firstIndex; + byte* src = number.DigitsPtr + firstIndex; uint remaining = lastIndex - firstIndex; while (remaining != 0) @@ -974,7 +974,7 @@ private static ulong NumberToFloatingPointBits(ref NumberBuffer number) { Debug.Assert(TFloat.DenormalMantissaBits <= FloatingPointMaxDenormalMantissaBits); - Debug.Assert(number.GetDigitsPointer()[0] != '0'); + Debug.Assert(number.DigitsPtr[0] != '0'); Debug.Assert(number.Scale <= FloatingPointMaxExponent); Debug.Assert(number.Scale >= FloatingPointMinExponent); @@ -998,7 +998,7 @@ private static ulong NumberToFloatingPointBits(ref NumberBuffer number) // Above 19 digits, we rely on slow path if (totalDigits <= 19) { - byte* src = number.GetDigitsPointer(); + byte* src = number.DigitsPtr; ulong mantissa = DigitsToUInt64(src, (int)(totalDigits)); diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index 952733c9268df..935328a21d20e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -113,7 +113,7 @@ private static unsafe bool TryNumberBufferToBinaryInteger(ref NumberBu return false; } - byte* p = number.GetDigitsPointer(); + byte* p = number.DigitsPtr; Debug.Assert(p != null); TInteger n = TInteger.Zero; @@ -725,7 +725,7 @@ internal static unsafe bool TryNumberToDecimal(ref NumberBuffer number, ref deci { number.CheckConsistency(); - byte* p = number.GetDigitsPointer(); + byte* p = number.DigitsPtr; int e = number.Scale; bool sign = number.IsNegative; uint c = *p; diff --git a/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs b/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs index 9037e4110817e..6b59ac75e5766 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ReadOnlyMemory.cs @@ -313,6 +313,7 @@ public unsafe MemoryHandle Pin() { if (typeof(T) == typeof(char) && tmpObject is string s) { + // Unsafe.AsPointer is safe since the handle pins it GCHandle handle = GCHandle.Alloc(tmpObject, GCHandleType.Pinned); ref char stringData = ref Unsafe.Add(ref s.GetRawStringData(), _index); return new MemoryHandle(Unsafe.AsPointer(ref stringData), handle); @@ -325,11 +326,13 @@ public unsafe MemoryHandle Pin() // Array is already pre-pinned if (_index < 0) { + // Unsafe.AsPointer is safe since it's pinned void* pointer = Unsafe.Add(Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(tmpObject))), _index & RemoveFlagsBitMask); return new MemoryHandle(pointer); } else { + // Unsafe.AsPointer is safe since the handle pins it GCHandle handle = GCHandle.Alloc(tmpObject, GCHandleType.Pinned); void* pointer = Unsafe.Add(Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(Unsafe.As(tmpObject))), _index); return new MemoryHandle(pointer, handle); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs index b6dc25bb43643..901d354cfc7c8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs @@ -3,6 +3,7 @@ #pragma warning disable IDE0060 // implementations provided as intrinsics using System; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.Versioning; @@ -907,5 +908,32 @@ public static ref T Unbox(object box) // unbox !!T // ret } + + + // Internal helper methods: + + // Determines if the address is aligned at least to `alignment` bytes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsOpportunisticallyAligned(ref readonly T address, nuint alignment) + { + // `alignment` is expected to be a power of 2 in bytes. + // We use Unsafe.AsPointer to convert to a pointer, + // GC will keep alignment when moving objects (up to sizeof(void*)), + // otherwise alignment should be considered a hint if not pinned. + Debug.Assert(nuint.IsPow2(alignment)); + return ((nuint)AsPointer(ref AsRef(in address)) & (alignment - 1)) == 0; + } + + // Determines the misalignment of the address with respect to the specified `alignment`. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static nuint OpportunisticMisalignment(ref readonly T address, nuint alignment) + { + // `alignment` is expected to be a power of 2 in bytes. + // We use Unsafe.AsPointer to convert to a pointer, + // GC will keep alignment when moving objects (up to sizeof(void*)), + // otherwise alignment should be considered a hint if not pinned. + Debug.Assert(nuint.IsPow2(alignment)); + return (nuint)AsPointer(ref AsRef(in address)) & (alignment - 1); + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs index 7a45e868a3d02..14151c1c02705 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.cs @@ -127,6 +127,7 @@ public readonly IntPtr AddrOfPinnedObject() unsafe { + // Unsafe.AsPointer calls are safe since object is pinned. if (RuntimeHelpers.ObjectHasComponentSize(target)) { if (target.GetType() == typeof(string)) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs index 109efc9da4471..74ef8257749aa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs @@ -165,6 +165,7 @@ public static unsafe IntPtr UnsafeAddrOfPinnedArrayElement(Array arr, int index) { ArgumentNullException.ThrowIfNull(arr); + // Unsafe.AsPointer is safe since array must be pinned void* pRawData = Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(arr)); return (IntPtr)((byte*)pRawData + (uint)index * (nuint)arr.GetElementSize()); } @@ -173,6 +174,7 @@ public static unsafe IntPtr UnsafeAddrOfPinnedArrayElement(T[] arr, int index { ArgumentNullException.ThrowIfNull(arr); + // Unsafe.AsPointer is safe since array must be pinned void* pRawData = Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(arr)); #pragma warning disable 8500 // sizeof of managed types return (IntPtr)((byte*)pRawData + (uint)index * (nuint)sizeof(T)); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs index 4588f6912efd8..34cfccd08755d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/AnsiStringMarshaller.cs @@ -87,6 +87,7 @@ public void FromManaged(string? managed, Span buffer) } } + // Unsafe.AsPointer is safe since buffer must be pinned _unmanagedValue = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); Marshal.GetAnsiStringBytes(managed, buffer); // Includes null terminator diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/ArrayMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/ArrayMarshaller.cs index 0800b623d449e..823c31917e764 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/ArrayMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/ArrayMarshaller.cs @@ -173,7 +173,11 @@ public void FromManaged(T[]? array, Span buffer) /// Returns the unmanaged value representing the array. /// /// A pointer to the beginning of the unmanaged value. - public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)Unsafe.AsPointer(ref GetPinnableReference()); + public TUnmanagedElement* ToUnmanaged() + { + // Unsafe.AsPointer is safe since buffer must be pinned + return (TUnmanagedElement*)Unsafe.AsPointer(ref GetPinnableReference()); + } /// /// Frees resources. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs index 38b033d0a9bd7..561f8ce8de312 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/BStrStringMarshaller.cs @@ -86,6 +86,7 @@ public void FromManaged(string? managed, Span buffer) else { // Set length and update buffer target + // Unsafe.AsPointer is safe since buffer must be pinned byte* pBuffer = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); *((uint*)pBuffer) = (uint)lengthInBytes; ptrToFirstChar = (ushort*)(pBuffer + sizeof(uint)); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/PointerArrayMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/PointerArrayMarshaller.cs index ee7a3a134229c..846879583d5ac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/PointerArrayMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/PointerArrayMarshaller.cs @@ -174,7 +174,11 @@ public void FromManaged(T*[]? array, Span buffer) /// Returns the unmanaged value representing the array. /// /// A pointer to the beginning of the unmanaged value. - public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)Unsafe.AsPointer(ref GetPinnableReference()); + public TUnmanagedElement* ToUnmanaged() + { + // Unsafe.AsPointer is safe since buffer must be pinned + return (TUnmanagedElement*)Unsafe.AsPointer(ref GetPinnableReference()); + } /// /// Frees resources. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/ReadOnlySpanMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/ReadOnlySpanMarshaller.cs index 8fe502608dce6..bab60b629e319 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/ReadOnlySpanMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/ReadOnlySpanMarshaller.cs @@ -142,7 +142,11 @@ public void FromManaged(ReadOnlySpan managed, Span buffer) /// /// Returns the unmanaged value representing the array. /// - public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)Unsafe.AsPointer(ref GetPinnableReference()); + public TUnmanagedElement* ToUnmanaged() + { + // Unsafe.AsPointer is safe since buffer must be pinned + return (TUnmanagedElement*)Unsafe.AsPointer(ref GetPinnableReference()); + } /// /// Frees resources. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/SpanMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/SpanMarshaller.cs index a9d42299848df..fb8aa49b12b61 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/SpanMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/SpanMarshaller.cs @@ -170,7 +170,11 @@ public void FromManaged(Span managed, Span buffer) /// /// Returns the unmanaged value representing the array. /// - public TUnmanagedElement* ToUnmanaged() => (TUnmanagedElement*)Unsafe.AsPointer(ref GetPinnableReference()); + public TUnmanagedElement* ToUnmanaged() + { + // Unsafe.AsPointer is safe since buffer must be pinned + return (TUnmanagedElement*)Unsafe.AsPointer(ref GetPinnableReference()); + } /// /// Frees resources. diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs index e6d529392bc9e..ee231616eaad2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshalling/Utf8StringMarshaller.cs @@ -91,6 +91,7 @@ public void FromManaged(string? managed, Span buffer) } } + // Unsafe.AsPointer is safe since buffer must be pinned _unmanagedValue = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); int byteCount = Encoding.UTF8.GetBytes(managed, buffer); diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.ByteMemOps.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.ByteMemOps.cs index 5429459209925..b4aa563b27747 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.ByteMemOps.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.ByteMemOps.cs @@ -162,7 +162,7 @@ internal static unsafe void Memmove(ref byte dest, ref byte src, nuint len) // // dest is more important to align than src because an unaligned store is more expensive // than an unaligned load. - nuint misalignedElements = 64 - (nuint)Unsafe.AsPointer(ref dest) & 63; + nuint misalignedElements = 64 - Unsafe.OpportunisticMisalignment(ref dest, 64); Unsafe.As(ref dest) = Unsafe.As(ref src); src = ref Unsafe.Add(ref src, misalignedElements); dest = ref Unsafe.Add(ref dest, misalignedElements); @@ -366,7 +366,7 @@ public static unsafe void ClearWithoutReferences(ref byte dest, nuint len) { // Try to opportunistically align the destination below. The input isn't pinned, so the GC // is free to move the references. We're therefore assuming that reads may still be unaligned. - nuint misalignedElements = 64 - (nuint)Unsafe.AsPointer(ref dest) & 63; + nuint misalignedElements = 64 - Unsafe.OpportunisticMisalignment(ref dest, 64); Unsafe.WriteUnaligned(ref dest, default); dest = ref Unsafe.Add(ref dest, misalignedElements); len -= misalignedElements; diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.cs index aa7ed473d9fef..a0e0b266589bd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.cs @@ -14,7 +14,7 @@ internal static partial class SpanHelpers { public static unsafe void ClearWithReferences(ref IntPtr ip, nuint pointerSizeLength) { - Debug.Assert((int)Unsafe.AsPointer(ref ip) % sizeof(IntPtr) == 0, "Should've been aligned on natural word boundary."); + Debug.Assert(Unsafe.IsOpportunisticallyAligned(ref ip, (uint)sizeof(IntPtr)), "Should've been aligned on natural word boundary."); // First write backward 8 natural words at a time. // Writing backward allows us to get away with only simple modifications to the diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.cs index 85801e101a173..5507aed9c7700 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.cs @@ -141,7 +141,7 @@ private static unsafe bool IsValidCore(ref T searchSpace, int length) where T // Try to opportunistically align the reads below. The input isn't pinned, so the GC // is free to move the references. We're therefore assuming that reads may still be unaligned. // They may also be unaligned if the input chars aren't 2-byte aligned. - nuint misalignedElements = ((nuint)Unsafe.AsPointer(ref searchSpace) & (nuint)(Vector256.Count - 1)) / (nuint)sizeof(T); + nuint misalignedElements = Unsafe.OpportunisticMisalignment(ref searchSpace, (uint)Vector256.Count) / (nuint)sizeof(T); i -= misalignedElements; Debug.Assert((int)i > 3 * Vector256.Count); @@ -193,7 +193,7 @@ private static unsafe bool IsValidCore(ref T searchSpace, int length) where T // Try to opportunistically align the reads below. The input isn't pinned, so the GC // is free to move the references. We're therefore assuming that reads may still be unaligned. // They may also be unaligned if the input chars aren't 2-byte aligned. - nuint misalignedElements = ((nuint)Unsafe.AsPointer(ref searchSpace) & (nuint)(Vector128.Count - 1)) / (nuint)sizeof(T); + nuint misalignedElements = Unsafe.OpportunisticMisalignment(ref searchSpace, (uint)Vector128.Count) / (nuint)sizeof(T); i -= misalignedElements; Debug.Assert((int)i > 3 * Vector128.Count); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Lock.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Lock.cs index 056ff96d41b1e..c3d7f633af43c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Lock.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Lock.cs @@ -636,15 +636,6 @@ internal nint LockIdForEvents } } - internal unsafe nint ObjectIdForEvents - { - get - { - Lock lockObj = this; - return *(nint*)Unsafe.AsPointer(ref lockObj); - } - } - internal ulong OwningThreadId => _owningThreadId; private static short DetermineMaxSpinCount() => diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TplEventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TplEventSource.cs index c80e3f300e5d8..048306cdf5076 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TplEventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TplEventSource.cs @@ -502,7 +502,7 @@ public void TraceSynchronousWorkEnd(CausalitySynchronousWork Work) } [NonEvent] - public unsafe void RunningContinuation(int TaskID, object Object) { RunningContinuation(TaskID, (long)*((void**)Unsafe.AsPointer(ref Object))); } + public unsafe void RunningContinuation(int TaskID, object Object) => RunningContinuation(TaskID, ObjectIDForEvents(Object)); [Event(20, Keywords = Keywords.Debug)] private void RunningContinuation(int TaskID, long Object) { @@ -511,7 +511,7 @@ private void RunningContinuation(int TaskID, long Object) } [NonEvent] - public unsafe void RunningContinuationList(int TaskID, int Index, object Object) { RunningContinuationList(TaskID, Index, (long)*((void**)Unsafe.AsPointer(ref Object))); } + public unsafe void RunningContinuationList(int TaskID, int Index, object Object) => RunningContinuationList(TaskID, Index, ObjectIDForEvents(Object)); [Event(21, Keywords = Keywords.Debug)] public void RunningContinuationList(int TaskID, int Index, long Object) diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs index eaac2608d8184..1347a2858e372 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatefulPinnedMarshalling.cs @@ -86,6 +86,7 @@ public ref StatefulPinnedNative GetPinnableReference() _canFree = true; if (_isPinned) { + // Unsafe.AsPointer is safe, because the result from GetPinnableReference is pinned _refNativeStruct = new StatefulPinnedNative() { I = _managed.I }; return (StatefulPinnedNative*)Unsafe.AsPointer(ref _refNativeStruct); } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs index f8b1174799b13..6388ab2990113 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/ComInterfaces/IStatelessCallerAllocateBufferMarshalling.cs @@ -94,6 +94,7 @@ public static class ManagedToUnmanagedIn { var unmanaged = new StatelessCallerAllocatedBufferNative() { I = managed.I }; MemoryMarshal.Write(buffer, in unmanaged); + // Unsafe.AsPointer is safe since buffer is pinned return (StatelessCallerAllocatedBufferNative*)Unsafe.AsPointer(ref MemoryMarshal.AsRef(buffer)); } diff --git a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs index 88e594d9f6a2d..aa4df06e002f4 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/TestAssets/SharedTypes/NonBlittable.cs @@ -84,6 +84,7 @@ public static unsafe class DoubleToBytesBigEndianMarshaller public static byte* ConvertToUnmanaged(double managed, Span buffer) { + // Unsafe.AsPointer is safe since buffer must be pinned BinaryPrimitives.WriteDoubleBigEndian(buffer, managed); return (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); } @@ -305,6 +306,7 @@ public struct StatefulGetPinnableReference private IntWrapperWithoutGetPinnableReference _managed; public void FromManaged(IntWrapperWithoutGetPinnableReference managed) => _managed = managed; + // Unsafe.AsPointer is safe since buffer must be pinned public int* ToUnmanaged() => (int*)Unsafe.AsPointer(ref _managed.i); public ref int GetPinnableReference() => ref _managed.i; @@ -463,6 +465,7 @@ public unsafe static class ListMarshallerWithBuffer where if (spaceRequired > buffer.Length) throw new InvalidOperationException(); + // Unsafe.AsPointer is safe since buffer must be pinned return (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(buffer)); } @@ -520,6 +523,7 @@ public void FromManaged(List managed, Span buffer) public ref TUnmanagedElement GetPinnableReference() => ref MemoryMarshal.GetReference(_span); + // Unsafe.AsPointer is safe since buffer must be pinned public byte* ToUnmanaged() => (byte*)Unsafe.AsPointer(ref GetPinnableReference()); public Span GetManagedValuesDestination(int length) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/GCTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/GCTests.cs index 59cedd9a5cea6..e4aad71c5ac35 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/GCTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/GCTests.cs @@ -1096,7 +1096,7 @@ private unsafe static void AllocateArrayPinned_ManagedValueType_CanRoundtripThro var rng = new Random(0xAF); EmbeddedValueType[] array = uninitialized ? GC.AllocateUninitializedArray>(length, pinned: true) : GC.AllocateArray>(length, pinned: true); - byte* pointer = (byte*)Unsafe.AsPointer(ref array[0]); + byte* pointer = (byte*)Unsafe.AsPointer(ref array[0]); // Unsafe.AsPointer is safe since array is pinned var size = Unsafe.SizeOf>(); for(int i = 0; i < length; ++i) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs index dbd6bf7cbee5b..9f59853b136da 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/StringTests.cs @@ -999,6 +999,7 @@ public static unsafe void GetPinnableReference_ReturnsSameAsGCHandleAndLegacyFix GCHandle gcHandle = GCHandle.Alloc(input, GCHandleType.Pinned); try { + // Unsafe.AsPointer is safe since it's pinned by the gc handle Assert.Equal((IntPtr)Unsafe.AsPointer(ref Unsafe.AsRef(in rChar)), gcHandle.AddrOfPinnedObject()); } finally