diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs index c624d72ec71..a67da08dc16 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs @@ -62,7 +62,10 @@ namespace Microsoft.Android.Sdk.TrimmableTypeMap; /// JniNativeMethod* methods = stackalloc JniNativeMethod[2]; /// methods[0] = new JniNativeMethod(&__utf8_0, &__utf8_1, &n_OnCreate_uco_0); /// methods[1] = new JniNativeMethod(&__utf8_2, &__utf8_3, &nctor_0_uco); -/// JniEnvironment.Types.RegisterNatives(jniType.PeerReference, new ReadOnlySpan<JniNativeMethod>(methods, 2)); +/// unsafe { +/// // SAFETY: methods points to the stackalloc buffer filled above; the UTF-8 fields and UCO entry points are module-lifetime data. +/// JniEnvironment.Types.RegisterNatives(jniType.PeerReference, new ReadOnlySpan<JniNativeMethod>(methods, 2)); +/// } /// } /// } /// diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/ScannerHashingHelper.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/ScannerHashingHelper.cs index fe3aa9aaed0..163504d7a3c 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/ScannerHashingHelper.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/ScannerHashingHelper.cs @@ -12,7 +12,11 @@ internal static string ToLegacyCrc64 (string ns, string assemblyName) int byteCount = GetNamespaceAssemblyUtf8ByteCount (ns, assemblyName); byte[] rented = ArrayPool.Shared.Rent (byteCount); try { - int bytesWritten = GetNamespaceAssemblyUtf8Bytes (ns, assemblyName, rented.AsSpan (0, byteCount)); + int bytesWritten; + unsafe { + // SAFETY: rented is at least byteCount bytes, which is the exact UTF-8 byte count for the data written by GetNamespaceAssemblyUtf8Bytes. + bytesWritten = GetNamespaceAssemblyUtf8Bytes (ns, assemblyName, rented.AsSpan (0, byteCount)); + } ulong crc = ulong.MaxValue; ulong length = 0; Crc64Helper.HashCore (rented, 0, bytesWritten, ref crc, ref length); @@ -32,7 +36,11 @@ internal static string ToCrc64 (string ns, string assemblyName) ? stackalloc byte [stackallocThresholdBytes] : new byte [byteCount]; - int bytesWritten = GetNamespaceAssemblyUtf8Bytes (ns, assemblyName, utf8Buffer.Slice (0, byteCount)); + int bytesWritten; + unsafe { + // SAFETY: utf8Buffer is at least byteCount bytes, which is the exact UTF-8 byte count for the data written by GetNamespaceAssemblyUtf8Bytes. + bytesWritten = GetNamespaceAssemblyUtf8Bytes (ns, assemblyName, utf8Buffer.Slice (0, byteCount)); + } Span hash = stackalloc byte [8]; System.IO.Hashing.Crc64.Hash (utf8Buffer.Slice (0, bytesWritten), hash); ulong hashValue = BinaryPrimitives.ReadUInt64LittleEndian (hash); @@ -45,12 +53,16 @@ static int GetNamespaceAssemblyUtf8ByteCount (string ns, string assemblyName) return System.Text.Encoding.UTF8.GetByteCount (ns) + 1 + System.Text.Encoding.UTF8.GetByteCount (assemblyName); } + /// + /// must contain at least + /// bytes for and . + /// static unsafe int GetNamespaceAssemblyUtf8Bytes (string ns, string assemblyName, Span destination) { - int bytesWritten = 0; + int bytesWritten; fixed (char* nsPtr = ns) fixed (byte* destinationPtr = destination) { - bytesWritten += System.Text.Encoding.UTF8.GetBytes (nsPtr, ns.Length, destinationPtr, destination.Length); + bytesWritten = System.Text.Encoding.UTF8.GetBytes (nsPtr, ns.Length, destinationPtr, destination.Length); } destination [bytesWritten++] = (byte) ':'; diff --git a/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.cs b/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.cs index a574c00a066..f1d65285e86 100644 --- a/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.cs +++ b/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMap.cs @@ -129,7 +129,7 @@ static void InitializeCore (ITypeMap typeMap) } } - internal static unsafe void RegisterNativeMethods () + internal static void RegisterNativeMethods () { lock (s_initLock) { if (s_nativeMethodsRegistered) { @@ -142,10 +142,13 @@ internal static unsafe void RegisterNativeMethods () } using var runtimeClass = new JniType ("mono/android/Runtime"u8); - fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) { - var onRegisterNatives = (IntPtr)(delegate* unmanaged)&OnRegisterNatives; - var method = new JniNativeMethod (name, sig, onRegisterNatives); - JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]); + unsafe { + // SAFETY: the UTF-8 literals are pinned for the duration of RegisterNatives, and OnRegisterNatives is a static UCO entry point. + fixed (byte* name = "registerNatives"u8, sig = "(Ljava/lang/Class;)V"u8) { + var onRegisterNatives = (IntPtr)(delegate* unmanaged)&OnRegisterNatives; + var method = new JniNativeMethod (name, sig, onRegisterNatives); + JniEnvironment.Types.RegisterNatives (runtimeClass.PeerReference, [method]); + } } s_nativeMethodsRegistered = true; }