Skip to content

Commit 0333a90

Browse files
committed
big endian fix
1 parent 111033e commit 0333a90

File tree

9 files changed

+126
-29
lines changed

9 files changed

+126
-29
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,7 @@
614614
<Compile Include="Microsoft\Data\ProviderBase\DbConnectionPoolIdentity.cs" />
615615
<Compile Include="Microsoft\Data\SqlClient\AAsyncCallContext.cs" />
616616
<Compile Include="Microsoft\Data\SqlClient\AlwaysEncryptedHelperClasses.cs" />
617+
<Compile Include="Microsoft\Data\SqlClient\BitConverterCompat.cs"/>
617618
<Compile Include="Microsoft\Data\SqlClient\LocalDBAPI.cs" />
618619
<Compile Include="Microsoft\Data\SqlClient\Reliability\SqlConfigurableRetryLogicManager.NetCoreApp.cs" />
619620
<Compile Include="Microsoft\Data\SqlClient\SNI\ConcurrentQueueSemaphore.cs" />
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// See the LICENSE file in the project root for more information.
2+
3+
using System;
4+
5+
namespace Microsoft.Data.SqlClient
6+
{
7+
internal static class BitConverterCompat
8+
{
9+
public static unsafe int SingleToInt32Bits(float value)
10+
{
11+
return *(int*)(&value);
12+
}
13+
public static unsafe float Int32BitsToSingle(int value)
14+
{
15+
return *(float*)(&value);
16+
}
17+
}
18+
}

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.Buffers.Binary;
67
using System.Diagnostics;
78
using System.Net;
89
using System.Net.Security;
@@ -59,10 +60,11 @@ public void Read(byte[] bytes)
5960
{
6061
SMID = bytes[0];
6162
flags = bytes[1];
62-
sessionId = BitConverter.ToUInt16(bytes, 2);
63-
length = BitConverter.ToUInt32(bytes, 4) - SNISMUXHeader.HEADER_LENGTH;
64-
sequenceNumber = BitConverter.ToUInt32(bytes, 8);
65-
highwater = BitConverter.ToUInt32(bytes, 12);
63+
Span<byte> span = bytes.AsSpan();
64+
sessionId = BinaryPrimitives.ReadUInt16LittleEndian(span.Slice(2));
65+
length = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(4)) - SNISMUXHeader.HEADER_LENGTH;
66+
sequenceNumber = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(8));
67+
highwater = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(12));
6668
}
6769

6870
public void Write(Span<byte> bytes)

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.NetCoreApp.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,17 @@
44

55
using System;
66
using System.Diagnostics;
7+
using System.Buffers.Binary;
78

89
namespace Microsoft.Data.SqlClient
910
{
1011
internal sealed partial class TdsParser
1112
{
1213
internal static void FillGuidBytes(Guid guid, Span<byte> buffer) => guid.TryWriteBytes(buffer);
1314

14-
internal static void FillDoubleBytes(double value, Span<byte> buffer) => BitConverter.TryWriteBytes(buffer, value);
15+
internal static void FillDoubleBytes(double value, Span<byte> buffer) => BinaryPrimitives.TryWriteInt64LittleEndian(buffer, BitConverter.DoubleToInt64Bits(value));
1516

16-
internal static void FillFloatBytes(float v, Span<byte> buffer) => BitConverter.TryWriteBytes(buffer, v);
17+
internal static void FillFloatBytes(float value, Span<byte> buffer) => BinaryPrimitives.TryWriteInt32LittleEndian(buffer, BitConverterCompat.SingleToInt32Bits(value));
1718

1819
internal static Guid ConstructGuid(ReadOnlySpan<byte> bytes)
1920
{

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System;
66
using System.Buffers;
7+
using System.Buffers.Binary;
78
using System.Collections.Generic;
89
using System.Data;
910
using System.Data.SqlTypes;
@@ -1744,7 +1745,7 @@ internal void WriteInt(int v, TdsParserStateObject stateObj)
17441745
internal static void WriteInt(Span<byte> buffer, int value)
17451746
{
17461747
#if NETCOREAPP
1747-
BitConverter.TryWriteBytes(buffer, value);
1748+
BinaryPrimitives.TryWriteInt32LittleEndian(buffer, value);
17481749
#else
17491750
buffer[0] = (byte)(value & 0xff);
17501751
buffer[1] = (byte)((value >> 8) & 0xff);
@@ -1763,7 +1764,9 @@ internal byte[] SerializeFloat(float v)
17631764
throw ADP.ParameterValueOutOfRange(v.ToString());
17641765
}
17651766

1766-
return BitConverter.GetBytes(v);
1767+
var bytes = new byte[4];
1768+
BinaryPrimitives.WriteInt32LittleEndian(bytes, BitConverterCompat.SingleToInt32Bits(v));
1769+
return bytes;
17671770
}
17681771

17691772
internal void WriteFloat(float v, TdsParserStateObject stateObj)
@@ -1886,7 +1889,9 @@ internal byte[] SerializeDouble(double v)
18861889
throw ADP.ParameterValueOutOfRange(v.ToString());
18871890
}
18881891

1889-
return BitConverter.GetBytes(v);
1892+
var bytes = new byte[8];
1893+
BinaryPrimitives.WriteInt64LittleEndian(bytes, BitConverter.DoubleToInt64Bits(v));
1894+
return bytes;
18901895
}
18911896

18921897
internal void WriteDouble(double v, TdsParserStateObject stateObj)
@@ -3808,8 +3813,8 @@ private bool TryProcessFedAuthInfo(TdsParserStateObject stateObj, int tokenLen,
38083813
uint currentOptionOffset = checked(i * optionSize);
38093814

38103815
byte id = tokenData[currentOptionOffset];
3811-
uint dataLen = BitConverter.ToUInt32(tokenData, checked((int)(currentOptionOffset + 1)));
3812-
uint dataOffset = BitConverter.ToUInt32(tokenData, checked((int)(currentOptionOffset + 5)));
3816+
uint dataLen = BinaryPrimitives.ReadUInt32LittleEndian(tokenData.AsSpan(checked((int)(currentOptionOffset + 1))));
3817+
uint dataOffset = BinaryPrimitives.ReadUInt32LittleEndian(tokenData.AsSpan(checked((int)(currentOptionOffset + 5))));
38133818
if (SqlClientEventSource.Log.IsAdvancedTraceOn())
38143819
{
38153820
SqlClientEventSource.Log.AdvancedTraceEvent("<sc.TdsParser.TryProcessFedAuthInfo> FedAuthInfoOpt: ID={0}, DataLen={1}, Offset={2}", id, dataLen.ToString(CultureInfo.InvariantCulture), dataOffset.ToString(CultureInfo.InvariantCulture));
@@ -5771,7 +5776,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
57715776
return false;
57725777
}
57735778

5774-
longValue = BitConverter.ToInt64(unencryptedBytes, 0);
5779+
longValue = BinaryPrimitives.ReadInt64LittleEndian(unencryptedBytes);
57755780

57765781
if (tdsType == TdsEnums.SQLBIT ||
57775782
tdsType == TdsEnums.SQLBITN)
@@ -5809,7 +5814,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
58095814
return false;
58105815
}
58115816

5812-
singleValue = BitConverter.ToSingle(unencryptedBytes, 0);
5817+
singleValue = BitConverterCompat.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(unencryptedBytes));
58135818
value.Single = singleValue;
58145819
break;
58155820

@@ -5820,7 +5825,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
58205825
return false;
58215826
}
58225827

5823-
doubleValue = BitConverter.ToDouble(unencryptedBytes, 0);
5828+
doubleValue = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(unencryptedBytes));
58245829
value.Double = doubleValue;
58255830
break;
58265831

@@ -5837,8 +5842,8 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
58375842
return false;
58385843
}
58395844

5840-
mid = BitConverter.ToInt32(unencryptedBytes, 0);
5841-
lo = BitConverter.ToUInt32(unencryptedBytes, 4);
5845+
mid = BinaryPrimitives.ReadInt32LittleEndian(unencryptedBytes);
5846+
lo = BinaryPrimitives.ReadUInt32LittleEndian(unencryptedBytes.AsSpan(4));
58425847

58435848
long l = (((long)mid) << 0x20) + ((long)lo);
58445849
value.SetToMoney(l);
@@ -5875,8 +5880,8 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
58755880
return false;
58765881
}
58775882

5878-
daypart = BitConverter.ToInt32(unencryptedBytes, 0);
5879-
timepart = BitConverter.ToUInt32(unencryptedBytes, 4);
5883+
daypart = BinaryPrimitives.ReadInt32LittleEndian(unencryptedBytes);
5884+
timepart = BinaryPrimitives.ReadUInt32LittleEndian(unencryptedBytes.AsSpan(4));
58805885
value.SetToDateTime(daypart, (int)timepart);
58815886
break;
58825887

@@ -5919,10 +5924,11 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
59195924
length = checked((int)length - 1);
59205925
int[] bits = new int[4];
59215926
int decLength = length >> 2;
5927+
var span = unencryptedBytes.AsSpan();
59225928
for (int i = 0; i < decLength; i++)
59235929
{
59245930
// up to 16 bytes of data following the sign byte
5925-
bits[i] = BitConverter.ToInt32(unencryptedBytes, index);
5931+
bits[i] = BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index));
59265932
index += 4;
59275933
}
59285934
value.SetToDecimal(md.baseTI.precision, md.baseTI.scale, fPositive, bits);
@@ -7490,7 +7496,20 @@ internal Task WriteString(string s, int length, int offset, TdsParserStateObject
74907496

74917497
private static void CopyCharsToBytes(char[] source, int sourceOffset, byte[] dest, int destOffset, int charLength)
74927498
{
7493-
Buffer.BlockCopy(source, sourceOffset, dest, destOffset, charLength * ADP.CharSize);
7499+
if (!BitConverter.IsLittleEndian)
7500+
{
7501+
int desti = 0;
7502+
Span<byte> span = dest.AsSpan();
7503+
for (int srci = 0; srci < charLength; srci++)
7504+
{
7505+
BinaryPrimitives.WriteUInt16LittleEndian(span.Slice(desti + destOffset), (ushort)source[srci + sourceOffset]);
7506+
desti += 2;
7507+
}
7508+
}
7509+
else
7510+
{
7511+
Buffer.BlockCopy(source, sourceOffset, dest, destOffset, charLength * ADP.CharSize);
7512+
}
74947513
}
74957514

74967515
private static void CopyStringToBytes(string source, int sourceOffset, byte[] dest, int destOffset, int charLength)

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System;
66
using System.Diagnostics;
7+
using System.Buffers.Binary;
78
using System.Runtime.InteropServices;
89
using System.Security;
910
using System.Threading;
@@ -527,7 +528,7 @@ internal bool TryReadInt64(out long value)
527528
Debug.Assert(_bTmpRead + bytesRead == 8, "TryReadByteArray returned true without reading all data required");
528529
_bTmpRead = 0;
529530
AssertValidState();
530-
value = BitConverter.ToInt64(_bTmp, 0);
531+
value = BinaryPrimitives.ReadInt64LittleEndian(_bTmp);
531532
return true;
532533
}
533534
}
@@ -536,7 +537,7 @@ internal bool TryReadInt64(out long value)
536537
// The entire long is in the packet and in the buffer, so just return it
537538
// and take care of the counters.
538539

539-
value = BitConverter.ToInt64(_inBuff, _inBytesUsed);
540+
value = BinaryPrimitives.ReadInt64LittleEndian(_inBuff.AsSpan(_inBytesUsed));
540541

541542
_inBytesUsed += 8;
542543
_inBytesPacket -= 8;
@@ -605,7 +606,7 @@ internal bool TryReadUInt32(out uint value)
605606
Debug.Assert(_bTmpRead + bytesRead == 4, "TryReadByteArray returned true without reading all data required");
606607
_bTmpRead = 0;
607608
AssertValidState();
608-
value = BitConverter.ToUInt32(_bTmp, 0);
609+
value = BinaryPrimitives.ReadUInt32LittleEndian(_bTmp);
609610
return true;
610611
}
611612
}
@@ -614,7 +615,7 @@ internal bool TryReadUInt32(out uint value)
614615
// The entire int is in the packet and in the buffer, so just return it
615616
// and take care of the counters.
616617

617-
value = BitConverter.ToUInt32(_inBuff, _inBytesUsed);
618+
value = BinaryPrimitives.ReadUInt32LittleEndian(_inBuff.AsSpan(_inBytesUsed));
618619

619620
_inBytesUsed += 4;
620621
_inBytesPacket -= 4;
@@ -639,15 +640,15 @@ internal bool TryReadSingle(out float value)
639640
}
640641

641642
AssertValidState();
642-
value = BitConverter.ToSingle(_bTmp, 0);
643+
value = BitConverterCompat.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(_bTmp));
643644
return true;
644645
}
645646
else
646647
{
647648
// The entire float is in the packet and in the buffer, so just return it
648649
// and take care of the counters.
649650

650-
value = BitConverter.ToSingle(_inBuff, _inBytesUsed);
651+
value = BitConverterCompat.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(_inBuff.AsSpan(_inBytesUsed)));
651652

652653
_inBytesUsed += 4;
653654
_inBytesPacket -= 4;
@@ -672,15 +673,15 @@ internal bool TryReadDouble(out double value)
672673
}
673674

674675
AssertValidState();
675-
value = BitConverter.ToDouble(_bTmp, 0);
676+
value = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(_bTmp));
676677
return true;
677678
}
678679
else
679680
{
680681
// The entire double is in the packet and in the buffer, so just return it
681682
// and take care of the counters.
682683

683-
value = BitConverter.ToDouble(_inBuff, _inBytesUsed);
684+
value = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(_inBuff.AsSpan(_inBytesUsed)));
684685

685686
_inBytesUsed += 8;
686687
_inBytesPacket -= 8;
@@ -1793,6 +1794,15 @@ private void SetBufferSecureStrings()
17931794
str = Marshal.SecureStringToBSTR(_securePasswords[i]);
17941795
byte[] data = new byte[_securePasswords[i].Length * 2];
17951796
Marshal.Copy(str, data, 0, _securePasswords[i].Length * 2);
1797+
if (!BitConverter.IsLittleEndian)
1798+
{
1799+
Span<byte> span = data.AsSpan();
1800+
for (int ii = 0; ii < _securePasswords[i].Length * 2; ii += 2)
1801+
{
1802+
short value = BinaryPrimitives.ReadInt16LittleEndian(span.Slice(ii));
1803+
BinaryPrimitives.WriteInt16BigEndian(span.Slice(ii), value);
1804+
}
1805+
}
17961806
TdsParserStaticMethods.ObfuscatePassword(data);
17971807
data.CopyTo(_outBuff, _securePasswordOffsetsInBuffer[i]);
17981808
}
@@ -2294,7 +2304,8 @@ internal Task WritePacket(byte flushMode, bool canAccumulate = false)
22942304
// So we need to avoid this check prior to login completing
22952305
state == TdsParserState.OpenLoggedIn
22962306
&& !_bulkCopyOpperationInProgress // ignore the condition checking for bulk copy
2297-
&& _outBytesUsed == (_outputHeaderLen + BitConverter.ToInt32(_outBuff, _outputHeaderLen))
2307+
&& _outBytesUsed == (_outputHeaderLen +
2308+
BinaryPrimitives.ReadInt32LittleEndian(_outBuff.AsSpan(_outputHeaderLen)))
22982309
&& _outputPacketCount == 0
22992310
|| _outBytesUsed == _outputHeaderLen
23002311
&& _outputPacketCount == 0)

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.Buffers.Binary;
67
using System.Diagnostics;
78
using System.Text;
89
using System.Threading;
@@ -514,6 +515,15 @@ public override void Convert(byte[] bytes, int byteIndex, int byteCount, char[]
514515
completed = (bytesUsed == byteCount);
515516

516517
// BlockCopy uses offsets\length measured in bytes, not the actual array index
518+
if (!BitConverter.IsLittleEndian)
519+
{
520+
Span<byte> span = bytes.AsSpan();
521+
for (int ii = 0; ii < byteCount; ii += 2)
522+
{
523+
short value = BinaryPrimitives.ReadInt16LittleEndian(span.Slice(ii));
524+
BinaryPrimitives.WriteInt16BigEndian(span.Slice(ii), value);
525+
}
526+
}
517527
Buffer.BlockCopy(bytes, byteIndex, chars, charIndex * 2, bytesUsed);
518528
}
519529
}

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.Buffers.Binary;
67
using System.Collections.Generic;
78
using System.Diagnostics;
89
using System.Security;
@@ -527,6 +528,13 @@ internal bool TryReadChars(char[] chars, int charsOffset, int charsCount, out in
527528
}
528529
}
529530
}
531+
if (!BitConverter.IsLittleEndian)
532+
{
533+
for (int ii = charsOffset; ii < charsCopied + charsOffset; ii++)
534+
{
535+
chars[ii] = (char)BinaryPrimitives.ReverseEndianness((ushort)chars[ii]);
536+
}
537+
}
530538
return true;
531539
}
532540

0 commit comments

Comments
 (0)