From c37395208c35770a0203785b886f990ee5c12fcd Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Fri, 19 Mar 2021 13:00:15 +0100 Subject: [PATCH] Big-endian fix: GUID handling (#49813) * Fix Utf8Formatter.Guid on big-endian systems (first three fields are stored in native byte order, not always little-endian) * Use Guid constructor in BlobContentId.FromHash to remove endian-dependent code accessing the structure directly --- .../Text/Utf8Formatter/Utf8Formatter.Guid.cs | 44 +++++++++++++++++----- .../System/Reflection/Metadata/BlobContentId.cs | 25 +++++++----- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs index 4b366f7..888f5ab 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs @@ -109,7 +109,7 @@ namespace System.Buffers.Text DecomposedGuid guidAsBytes = default; guidAsBytes.Guid = value; - // When a GUID is blitted, the first three components are little-endian, and the last component is big-endian. + // When a GUID is blitted, the first three components are native-endian, and the last component is big-endian. // The line below forces the JIT to hoist the bounds check for the following segment. // The JIT will optimize away the read, but it cannot optimize away the bounds check @@ -117,10 +117,20 @@ namespace System.Buffers.Text // We use 8 instead of 7 so that we also capture the dash if we're asked to insert one. { _ = destination[8]; } - HexConverter.ToBytesBuffer(guidAsBytes.Byte03, destination, 0, HexConverter.Casing.Lower); - HexConverter.ToBytesBuffer(guidAsBytes.Byte02, destination, 2, HexConverter.Casing.Lower); - HexConverter.ToBytesBuffer(guidAsBytes.Byte01, destination, 4, HexConverter.Casing.Lower); - HexConverter.ToBytesBuffer(guidAsBytes.Byte00, destination, 6, HexConverter.Casing.Lower); + if (BitConverter.IsLittleEndian) + { + HexConverter.ToBytesBuffer(guidAsBytes.Byte03, destination, 0, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte02, destination, 2, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte01, destination, 4, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte00, destination, 6, HexConverter.Casing.Lower); + } + else + { + HexConverter.ToBytesBuffer(guidAsBytes.Byte00, destination, 0, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte01, destination, 2, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte02, destination, 4, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte03, destination, 6, HexConverter.Casing.Lower); + } if (flags < 0 /* use dash? */) { @@ -133,8 +143,16 @@ namespace System.Buffers.Text } { _ = destination[4]; } - HexConverter.ToBytesBuffer(guidAsBytes.Byte05, destination, 0, HexConverter.Casing.Lower); - HexConverter.ToBytesBuffer(guidAsBytes.Byte04, destination, 2, HexConverter.Casing.Lower); + if (BitConverter.IsLittleEndian) + { + HexConverter.ToBytesBuffer(guidAsBytes.Byte05, destination, 0, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte04, destination, 2, HexConverter.Casing.Lower); + } + else + { + HexConverter.ToBytesBuffer(guidAsBytes.Byte04, destination, 0, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte05, destination, 2, HexConverter.Casing.Lower); + } if (flags < 0 /* use dash? */) { @@ -147,8 +165,16 @@ namespace System.Buffers.Text } { _ = destination[4]; } - HexConverter.ToBytesBuffer(guidAsBytes.Byte07, destination, 0, HexConverter.Casing.Lower); - HexConverter.ToBytesBuffer(guidAsBytes.Byte06, destination, 2, HexConverter.Casing.Lower); + if (BitConverter.IsLittleEndian) + { + HexConverter.ToBytesBuffer(guidAsBytes.Byte07, destination, 0, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte06, destination, 2, HexConverter.Casing.Lower); + } + else + { + HexConverter.ToBytesBuffer(guidAsBytes.Byte06, destination, 0, HexConverter.Casing.Lower); + HexConverter.ToBytesBuffer(guidAsBytes.Byte07, destination, 2, HexConverter.Casing.Lower); + } if (flags < 0 /* use dash? */) { diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs index 270c911..0ca5a41 100644 --- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs +++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs @@ -52,7 +52,7 @@ namespace System.Reflection.Metadata return FromHash(ImmutableByteArrayInterop.DangerousGetUnderlyingArray(hashCode)!); } - public static unsafe BlobContentId FromHash(byte[] hashCode) + public static BlobContentId FromHash(byte[] hashCode) { const int minHashSize = 20; @@ -66,16 +66,23 @@ namespace System.Reflection.Metadata throw new ArgumentException(SR.Format(SR.HashTooShort, minHashSize), nameof(hashCode)); } - Guid guid = default(Guid); - byte* guidPtr = (byte*)&guid; - for (var i = 0; i < BlobUtilities.SizeOfGuid; i++) - { - guidPtr[i] = hashCode[i]; - } + // extract guid components from input data + uint a = ((uint)hashCode[3] << 24 | (uint)hashCode[2] << 16 | (uint)hashCode[1] << 8 | hashCode[0]); + ushort b = (ushort)((ushort)hashCode[5] << 8 | (ushort)hashCode[4]); + ushort c = (ushort)((ushort)hashCode[7] << 8 | (ushort)hashCode[6]); + byte d = hashCode[8]; + byte e = hashCode[9]; + byte f = hashCode[10]; + byte g = hashCode[11]; + byte h = hashCode[12]; + byte i = hashCode[13]; + byte j = hashCode[14]; + byte k = hashCode[15]; // modify the guid data so it decodes to the form of a "random" guid ala rfc4122 - guidPtr[7] = (byte)((guidPtr[7] & 0x0f) | (4 << 4)); - guidPtr[8] = (byte)((guidPtr[8] & 0x3f) | (2 << 6)); + c = (ushort)((c & 0x0fff) | (4 << 12)); + d = (byte)((d & 0x3f) | (2 << 6)); + Guid guid = new Guid((int)a, (short)b, (short)c, d, e, f, g, h, i, j, k); // compute a random-looking stamp from the remaining bits, but with the upper bit set uint stamp = 0x80000000u | ((uint)hashCode[19] << 24 | (uint)hashCode[18] << 16 | (uint)hashCode[17] << 8 | hashCode[16]); -- 2.7.4