From 4284affc9416cb6af4cac6522a6d5cef355a286d Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Tue, 25 Jun 2024 13:11:40 -0700 Subject: [PATCH 1/5] Add call to DrainData when forcing HasPendingData flag to false on Decryption failure. --- .../netcore/src/Microsoft/Data/SqlClient/TdsParser.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 337752a068..609fe91c04 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -6140,6 +6140,12 @@ internal bool TryReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, int length, T // call to decrypt column keys has failed. The data wont be decrypted. // Not setting the value to false, forces the driver to look for column value. // Packet received from Key Vault will throws invalid token header. + if (stateObj.HasPendingData) + { + // Drain the pending data now if forcing the HasPendingData flag to false since the + // SqlDataReader.TryCloseInternal will not be able to do so as it checks first if that flag is set. + DrainData(stateObj); + } stateObj.HasPendingData = false; } throw SQL.ColumnDecryptionFailed(columnName, null, e); From aaf4591294d1655f9509b2e9a0c3a64eaaa0d09b Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Tue, 25 Jun 2024 17:06:50 -0700 Subject: [PATCH 2/5] Found one more place where HasPendingData is set to false and but not calling DrainData. --- .../netcore/src/Microsoft/Data/SqlClient/TdsParser.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 609fe91c04..df4d13a2b6 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -3141,6 +3141,12 @@ private bool TryProcessDone(SqlCommand cmd, SqlDataReader reader, ref RunBehavio stateObj.HasReceivedError = false; if (stateObj._inBytesUsed >= stateObj._inBytesRead) { + if (stateObj.HasPendingData) + { + // Drain the pending data now if forcing the HasPendingData flag to false since the + // SqlDataReader.TryCloseInternal will not be able to do so as it checks first if that flag is set. + DrainData(stateObj); + } stateObj.HasPendingData = false; } } From 5fa56659d1e14611c3b83a475049f1086204b152 Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Wed, 26 Jun 2024 08:54:20 -0700 Subject: [PATCH 3/5] Remove additional DrainData as it slowed down the repro significantly. --- .../netcore/src/Microsoft/Data/SqlClient/TdsParser.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index df4d13a2b6..609fe91c04 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -3141,12 +3141,6 @@ private bool TryProcessDone(SqlCommand cmd, SqlDataReader reader, ref RunBehavio stateObj.HasReceivedError = false; if (stateObj._inBytesUsed >= stateObj._inBytesRead) { - if (stateObj.HasPendingData) - { - // Drain the pending data now if forcing the HasPendingData flag to false since the - // SqlDataReader.TryCloseInternal will not be able to do so as it checks first if that flag is set. - DrainData(stateObj); - } stateObj.HasPendingData = false; } } From 28928412e663c75c47da428366b4d9766174037d Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Thu, 27 Jun 2024 16:15:32 -0700 Subject: [PATCH 4/5] Add comment why draining data on Decrypt failure. --- .../netcore/src/Microsoft/Data/SqlClient/TdsParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 609fe91c04..6af6ae7734 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -6142,8 +6142,8 @@ internal bool TryReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, int length, T // Packet received from Key Vault will throws invalid token header. if (stateObj.HasPendingData) { - // Drain the pending data now if forcing the HasPendingData flag to false since the - // SqlDataReader.TryCloseInternal will not be able to do so as it checks first if that flag is set. + // Drain the pending data now if setting the HasPendingData to false. + // SqlDataReader.TryCloseInternal can not drain if HasPendingData = false. DrainData(stateObj); } stateObj.HasPendingData = false; From 4afbd1a8501eedecff660f7963d062820a7e5a1e Mon Sep 17 00:00:00 2001 From: v-arellegue Date: Thu, 27 Jun 2024 16:28:59 -0700 Subject: [PATCH 5/5] Also, fix Tdsparser.cs in .netfx. --- .../netfx/src/Microsoft/Data/SqlClient/TdsParser.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index afa5220563..0c74f8f61b 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -6935,6 +6935,12 @@ internal bool TryReadSqlValue(SqlBuffer value, // call to decrypt column keys has failed. The data wont be decrypted. // Not setting the value to false, forces the driver to look for column value. // Packet received from Key Vault will throws invalid token header. + if (stateObj.HasPendingData) + { + // Drain the pending data now if setting the HasPendingData to false. + // SqlDataReader.TryCloseInternal can not drain if HasPendingData = false. + DrainData(stateObj); + } stateObj.HasPendingData = false; } throw SQL.ColumnDecryptionFailed(columnName, null, e);