Skip to content

Commit 08d42b1

Browse files
committed
big endian fix
1 parent 111033e commit 08d42b1

File tree

9 files changed

+113
-45
lines changed

9 files changed

+113
-45
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,7 @@
651651
<Compile Include="Microsoft\Data\SqlClient\TdsParser.cs" />
652652
<Compile Include="Microsoft\Data\SqlClient\TdsParser.RegisterEncoding.cs" />
653653
<Compile Include="Microsoft\Data\SqlClient\TdsParserHelperClasses.cs" />
654+
<Compile Include="Microsoft\Data\SqlClient\BitConverterCompat.cs"/>
654655
<Compile Include="Microsoft\Data\SqlClient\TdsParserStateObject.netcore.cs" />
655656
<Compile Include="Microsoft\Data\SqlClient\TdsParserStateObjectManaged.cs" />
656657
</ItemGroup>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
3+
namespace Microsoft.Data.SqlClient
4+
{
5+
internal static class BitConverterCompat
6+
{
7+
public static unsafe int SingleToInt32Bits(float value)
8+
{
9+
return *(int*)(&value);
10+
}
11+
public static unsafe float Int32BitsToSingle(int value)
12+
{
13+
return *(float*)(&value);
14+
}
15+
}
16+
}

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

+5-4
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,10 @@ 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+
sessionId = BinaryPrimitives.ReadUInt16LittleEndian(new ReadOnlySpan<byte>(bytes, 2, 2));
64+
length = BinaryPrimitives.ReadUInt32LittleEndian(new ReadOnlySpan<byte>(bytes, 4, 4)) - SNISMUXHeader.HEADER_LENGTH;
65+
sequenceNumber = BinaryPrimitives.ReadUInt32LittleEndian(new ReadOnlySpan<byte>(bytes, 8, 4));
66+
highwater = BinaryPrimitives.ReadUInt32LittleEndian(new ReadOnlySpan<byte>(bytes, 12, 4));
6667
}
6768

6869
public void Write(Span<byte> bytes)

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

+15-2
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,29 @@
44

55
using System;
66
using System.Diagnostics;
7+
#if NETCOREAPP
8+
using System.Buffers.Binary;
9+
#endif
710

811
namespace Microsoft.Data.SqlClient
912
{
1013
internal sealed partial class TdsParser
1114
{
1215
internal static void FillGuidBytes(Guid guid, Span<byte> buffer) => guid.TryWriteBytes(buffer);
1316

14-
internal static void FillDoubleBytes(double value, Span<byte> buffer) => BitConverter.TryWriteBytes(buffer, value);
17+
internal static void FillDoubleBytes(double value, Span<byte> buffer) =>
18+
#if NETCOREAPP
19+
BinaryPrimitives.WriteDoubleLittleEndian(buffer, value);
20+
#else
21+
BitConverter.TryWriteBytes(buffer, value);
22+
#endif
1523

16-
internal static void FillFloatBytes(float v, Span<byte> buffer) => BitConverter.TryWriteBytes(buffer, v);
24+
internal static void FillFloatBytes(float v, Span<byte> buffer) =>
25+
#if NETCOREAPP
26+
BinaryPrimitives.WriteSingleLittleEndian(buffer, v);
27+
#else
28+
BitConverter.TryWriteBytes(buffer, v);
29+
#endif
1730

1831
internal static Guid ConstructGuid(ReadOnlySpan<byte> bytes)
1932
{

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

+31-20
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
4-
54
using System;
65
using System.Buffers;
6+
using System.Buffers.Binary;
77
using System.Collections.Generic;
88
using System.Data;
99
using System.Data.SqlTypes;
@@ -1743,14 +1743,10 @@ internal void WriteInt(int v, TdsParserStateObject stateObj)
17431743

17441744
internal static void WriteInt(Span<byte> buffer, int value)
17451745
{
1746-
#if NETCOREAPP
1747-
BitConverter.TryWriteBytes(buffer, value);
1748-
#else
17491746
buffer[0] = (byte)(value & 0xff);
17501747
buffer[1] = (byte)((value >> 8) & 0xff);
17511748
buffer[2] = (byte)((value >> 16) & 0xff);
17521749
buffer[3] = (byte)((value >> 24) & 0xff);
1753-
#endif
17541750
}
17551751

17561752
//
@@ -1763,7 +1759,9 @@ internal byte[] SerializeFloat(float v)
17631759
throw ADP.ParameterValueOutOfRange(v.ToString());
17641760
}
17651761

1766-
return BitConverter.GetBytes(v);
1762+
var bytes = new byte[4];
1763+
BinaryPrimitives.WriteInt32LittleEndian(bytes, BitConverterCompat.SingleToInt32Bits(v));
1764+
return bytes;
17671765
}
17681766

17691767
internal void WriteFloat(float v, TdsParserStateObject stateObj)
@@ -1886,7 +1884,9 @@ internal byte[] SerializeDouble(double v)
18861884
throw ADP.ParameterValueOutOfRange(v.ToString());
18871885
}
18881886

1889-
return BitConverter.GetBytes(v);
1887+
var bytes = new byte[8];
1888+
BinaryPrimitives.WriteInt64LittleEndian(bytes, BitConverter.DoubleToInt64Bits(v));
1889+
return bytes;
18901890
}
18911891

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

38103810
byte id = tokenData[currentOptionOffset];
3811-
uint dataLen = BitConverter.ToUInt32(tokenData, checked((int)(currentOptionOffset + 1)));
3812-
uint dataOffset = BitConverter.ToUInt32(tokenData, checked((int)(currentOptionOffset + 5)));
3811+
uint dataLen = BinaryPrimitives.ReadUInt32LittleEndian(new ReadOnlySpan<byte>(tokenData, checked((int)(currentOptionOffset + 1)), 4));
3812+
uint dataOffset = BinaryPrimitives.ReadUInt32LittleEndian(new ReadOnlySpan<byte>(tokenData, checked((int)(currentOptionOffset + 5)), 4));
38133813
if (SqlClientEventSource.Log.IsAdvancedTraceOn())
38143814
{
38153815
SqlClientEventSource.Log.AdvancedTraceEvent("<sc.TdsParser.TryProcessFedAuthInfo> FedAuthInfoOpt: ID={0}, DataLen={1}, Offset={2}", id, dataLen.ToString(CultureInfo.InvariantCulture), dataOffset.ToString(CultureInfo.InvariantCulture));
@@ -5771,7 +5771,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
57715771
return false;
57725772
}
57735773

5774-
longValue = BitConverter.ToInt64(unencryptedBytes, 0);
5774+
longValue = BinaryPrimitives.ReadInt64LittleEndian(new ReadOnlySpan<byte>(unencryptedBytes, 0, 8));
57755775

57765776
if (tdsType == TdsEnums.SQLBIT ||
57775777
tdsType == TdsEnums.SQLBITN)
@@ -5808,8 +5808,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
58085808
{
58095809
return false;
58105810
}
5811-
5812-
singleValue = BitConverter.ToSingle(unencryptedBytes, 0);
5811+
singleValue = BitConverterCompat.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(unencryptedBytes));
58135812
value.Single = singleValue;
58145813
break;
58155814

@@ -5820,7 +5819,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
58205819
return false;
58215820
}
58225821

5823-
doubleValue = BitConverter.ToDouble(unencryptedBytes, 0);
5822+
doubleValue = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(unencryptedBytes));
58245823
value.Double = doubleValue;
58255824
break;
58265825

@@ -5837,8 +5836,8 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
58375836
return false;
58385837
}
58395838

5840-
mid = BitConverter.ToInt32(unencryptedBytes, 0);
5841-
lo = BitConverter.ToUInt32(unencryptedBytes, 4);
5839+
mid = BinaryPrimitives.ReadInt32LittleEndian(new ReadOnlySpan<byte>(unencryptedBytes, 0, 4));
5840+
lo = BinaryPrimitives.ReadUInt32LittleEndian(new ReadOnlySpan<byte>(unencryptedBytes, 4, 4));
58425841

58435842
long l = (((long)mid) << 0x20) + ((long)lo);
58445843
value.SetToMoney(l);
@@ -5875,8 +5874,8 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
58755874
return false;
58765875
}
58775876

5878-
daypart = BitConverter.ToInt32(unencryptedBytes, 0);
5879-
timepart = BitConverter.ToUInt32(unencryptedBytes, 4);
5877+
daypart = BinaryPrimitives.ReadInt32LittleEndian(new ReadOnlySpan<byte>(unencryptedBytes, 0, 4));
5878+
timepart = BinaryPrimitives.ReadUInt32LittleEndian(new ReadOnlySpan<byte>(unencryptedBytes, 4, 4));
58805879
value.SetToDateTime(daypart, (int)timepart);
58815880
break;
58825881

@@ -5922,7 +5921,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
59225921
for (int i = 0; i < decLength; i++)
59235922
{
59245923
// up to 16 bytes of data following the sign byte
5925-
bits[i] = BitConverter.ToInt32(unencryptedBytes, index);
5924+
bits[i] = BinaryPrimitives.ReadInt32LittleEndian(new ReadOnlySpan<byte>(unencryptedBytes, index, 4));
59265925
index += 4;
59275926
}
59285927
value.SetToDecimal(md.baseTI.precision, md.baseTI.scale, fPositive, bits);
@@ -7490,7 +7489,20 @@ internal Task WriteString(string s, int length, int offset, TdsParserStateObject
74907489

74917490
private static void CopyCharsToBytes(char[] source, int sourceOffset, byte[] dest, int destOffset, int charLength)
74927491
{
7493-
Buffer.BlockCopy(source, sourceOffset, dest, destOffset, charLength * ADP.CharSize);
7492+
if (!BitConverter.IsLittleEndian)
7493+
{
7494+
int desti = 0;
7495+
for(int srci = 0; srci < charLength; srci++)
7496+
{
7497+
dest[desti + destOffset] = (byte)(source[srci + sourceOffset]);
7498+
dest[desti + destOffset+1] = (byte)(source[srci + sourceOffset] >> 8);
7499+
desti += 2;
7500+
}
7501+
}
7502+
else
7503+
{
7504+
Buffer.BlockCopy(source, sourceOffset, dest, destOffset, charLength * ADP.CharSize);
7505+
}
74947506
}
74957507

74967508
private static void CopyStringToBytes(string source, int sourceOffset, byte[] dest, int destOffset, int charLength)
@@ -12571,7 +12583,6 @@ private bool TryReadPlpUnicodeCharsChunk(char[] buff, int offst, int len, TdsPar
1257112583
{
1257212584
charsToRead = (int)(stateObj._longlenleft >> 1);
1257312585
}
12574-
1257512586
if (!stateObj.TryReadChars(buff, offst, charsToRead, out charsRead))
1257612587
{
1257712588
charsRead = 0;

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

+21-14
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,8 +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-
540+
value = BinaryPrimitives.ReadInt64LittleEndian(new ReadOnlySpan<byte>(_inBuff, _inBytesUsed, 8));
541541
_inBytesUsed += 8;
542542
_inBytesPacket -= 8;
543543

@@ -605,7 +605,7 @@ internal bool TryReadUInt32(out uint value)
605605
Debug.Assert(_bTmpRead + bytesRead == 4, "TryReadByteArray returned true without reading all data required");
606606
_bTmpRead = 0;
607607
AssertValidState();
608-
value = BitConverter.ToUInt32(_bTmp, 0);
608+
value = BinaryPrimitives.ReadUInt32LittleEndian(_bTmp);
609609
return true;
610610
}
611611
}
@@ -614,7 +614,7 @@ internal bool TryReadUInt32(out uint value)
614614
// The entire int is in the packet and in the buffer, so just return it
615615
// and take care of the counters.
616616

617-
value = BitConverter.ToUInt32(_inBuff, _inBytesUsed);
617+
value = BinaryPrimitives.ReadUInt32LittleEndian(new ReadOnlySpan<byte>(_inBuff, _inBytesUsed, 4));
618618

619619
_inBytesUsed += 4;
620620
_inBytesPacket -= 4;
@@ -639,16 +639,14 @@ internal bool TryReadSingle(out float value)
639639
}
640640

641641
AssertValidState();
642-
value = BitConverter.ToSingle(_bTmp, 0);
642+
value = BitConverterCompat.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(_bTmp));
643643
return true;
644644
}
645645
else
646646
{
647647
// The entire float is in the packet and in the buffer, so just return it
648648
// and take care of the counters.
649-
650-
value = BitConverter.ToSingle(_inBuff, _inBytesUsed);
651-
649+
value = BitConverterCompat.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(new ReadOnlySpan<byte>(_inBuff, _inBytesUsed, 4)));
652650
_inBytesUsed += 4;
653651
_inBytesPacket -= 4;
654652

@@ -672,16 +670,15 @@ internal bool TryReadDouble(out double value)
672670
}
673671

674672
AssertValidState();
675-
value = BitConverter.ToDouble(_bTmp, 0);
673+
value = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(_bTmp));
676674
return true;
677675
}
678676
else
679677
{
680678
// The entire double is in the packet and in the buffer, so just return it
681679
// and take care of the counters.
682680

683-
value = BitConverter.ToDouble(_inBuff, _inBytesUsed);
684-
681+
value = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(new ReadOnlySpan<byte>(_inBuff, _inBytesUsed, 8)));
685682
_inBytesUsed += 8;
686683
_inBytesPacket -= 8;
687684

@@ -1793,6 +1790,16 @@ private void SetBufferSecureStrings()
17931790
str = Marshal.SecureStringToBSTR(_securePasswords[i]);
17941791
byte[] data = new byte[_securePasswords[i].Length * 2];
17951792
Marshal.Copy(str, data, 0, _securePasswords[i].Length * 2);
1793+
if (!BitConverter.IsLittleEndian)
1794+
{
1795+
byte temp;
1796+
for (int ii = 0; ii < _securePasswords[i].Length * 2; ii += 2)
1797+
{
1798+
temp = (byte)data[ii];
1799+
data[ii] = (byte)data[ii + 1];
1800+
data[ii + 1] = (byte)temp;
1801+
}
1802+
}
17961803
TdsParserStaticMethods.ObfuscatePassword(data);
17971804
data.CopyTo(_outBuff, _securePasswordOffsetsInBuffer[i]);
17981805
}
@@ -2059,7 +2066,6 @@ internal void WriteSecureString(SecureString secureString)
20592066
Debug.Assert(_securePasswords[0] == null || _securePasswords[1] == null, "There are more than two secure passwords");
20602067

20612068
int index = _securePasswords[0] != null ? 1 : 0;
2062-
20632069
_securePasswords[index] = secureString;
20642070
_securePasswordOffsetsInBuffer[index] = _outBytesUsed;
20652071

@@ -2294,7 +2300,8 @@ internal Task WritePacket(byte flushMode, bool canAccumulate = false)
22942300
// So we need to avoid this check prior to login completing
22952301
state == TdsParserState.OpenLoggedIn
22962302
&& !_bulkCopyOpperationInProgress // ignore the condition checking for bulk copy
2297-
&& _outBytesUsed == (_outputHeaderLen + BitConverter.ToInt32(_outBuff, _outputHeaderLen))
2303+
&& _outBytesUsed == (_outputHeaderLen +
2304+
BinaryPrimitives.ReadInt32LittleEndian(new ReadOnlySpan<byte>(_outBuff, _outputHeaderLen, 4)))
22982305
&& _outputPacketCount == 0
22992306
|| _outBytesUsed == _outputHeaderLen
23002307
&& _outputPacketCount == 0)

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

+10
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,16 @@ public override void Convert(byte[] bytes, int byteIndex, int byteCount, char[]
514514
completed = (bytesUsed == byteCount);
515515

516516
// BlockCopy uses offsets\length measured in bytes, not the actual array index
517+
if (!BitConverter.IsLittleEndian)
518+
{
519+
byte temp;
520+
for (int ii = 0; ii < byteCount; ii += 2)
521+
{
522+
temp = bytes[ii];
523+
bytes[ii] = bytes[ii + 1];
524+
bytes[ii + 1] = temp;
525+
}
526+
}
517527
Buffer.BlockCopy(bytes, byteIndex, chars, charIndex * 2, bytesUsed);
518528
}
519529
}

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

+8
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)