From a9f9f77ef420d0c3691ec5bc308cd867962d9a0a Mon Sep 17 00:00:00 2001 From: Kyle Bouchard Date: Tue, 18 Aug 2026 09:48:42 -0400 Subject: [PATCH] fix(csharp): fix double free in C# wrapper --- wrappers/csharp/src/DecryptionStream.cs | 9 +++-- wrappers/csharp/src/EncryptionStream.cs | 9 +++-- .../csharp/tests/unit-tests/TestStreams.cs | 36 +++++++++++++++++++ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/wrappers/csharp/src/DecryptionStream.cs b/wrappers/csharp/src/DecryptionStream.cs index 2c3695f7..e5bb2352 100644 --- a/wrappers/csharp/src/DecryptionStream.cs +++ b/wrappers/csharp/src/DecryptionStream.cs @@ -101,6 +101,8 @@ public void FlushFinalBlock() throw new NotSupportedException(); } + _finalBlockTransformed = true; + if (_inputBufferOffset > 0) { byte[] outputBuffer = DecryptLastChunk(); @@ -109,8 +111,6 @@ public void FlushFinalBlock() } Array.Clear(_inputBuffer, 0, _inputBuffer.Length); - - _finalBlockTransformed = true; } public override void Flush() @@ -188,14 +188,13 @@ private byte[] DecryptLastChunk() long result = Native.OnlineDecryptorLastChunk(_native_ptr, _inputBuffer, (UIntPtr)_inputBufferOffset, aad, UIntPtr.Zero, outputBuffer, (UIntPtr)outputBuffer.Length); + _native_ptr = UIntPtr.Zero; + if (result < 0) { Utils.HandleError(result); } - // Here, the pointer is freed, so let's set it to 0 - _native_ptr = UIntPtr.Zero; - return outputBuffer; } diff --git a/wrappers/csharp/src/EncryptionStream.cs b/wrappers/csharp/src/EncryptionStream.cs index bce2968a..6ddfe62e 100644 --- a/wrappers/csharp/src/EncryptionStream.cs +++ b/wrappers/csharp/src/EncryptionStream.cs @@ -89,6 +89,8 @@ public void FlushFinalBlock() throw new NotSupportedException(); } + _finalBlockTransformed = true; + if (_inputBufferOffset > 0) { byte[] outputBuffer = EncryptLastChunk(); @@ -97,8 +99,6 @@ public void FlushFinalBlock() } Array.Clear(_inputBuffer, 0, _inputBuffer.Length); - - _finalBlockTransformed = true; } public override void Flush() @@ -175,14 +175,13 @@ private byte[] EncryptLastChunk() long result = Native.OnlineEncryptorLastChunk(_native_ptr, _inputBuffer, (UIntPtr)_inputBufferOffset, aad, UIntPtr.Zero, outputBuffer, (UIntPtr)outputBuffer.Length); + _native_ptr = UIntPtr.Zero; + if (result < 0) { Utils.HandleError(result); } - // Here, the pointer is freed, so let's set it to 0 - _native_ptr = UIntPtr.Zero; - return outputBuffer; } diff --git a/wrappers/csharp/tests/unit-tests/TestStreams.cs b/wrappers/csharp/tests/unit-tests/TestStreams.cs index 9f0d1b1b..d0385704 100644 --- a/wrappers/csharp/tests/unit-tests/TestStreams.cs +++ b/wrappers/csharp/tests/unit-tests/TestStreams.cs @@ -56,5 +56,41 @@ public void DecryptStream() Assert.IsTrue(result.Length == 1689); Assert.IsTrue(Utils.EncodeToBase64String(result) == TestData.Base64TestDataStream); } + + [TestMethod] + public void DecryptStreamTruncatedCiphertext() + { + const int ChunkLength = 8; + const int EncryptedChunkLength = ChunkLength + 16; + + byte[] plaintext = new byte[3 * ChunkLength]; + byte[] header; + byte[] ciphertext; + + using (MemoryStream encrypted = new MemoryStream()) + { + using (EncryptionStream ec = + new EncryptionStream(TestData.BytesTestKey, [], ChunkLength, false, 0, encrypted, true)) + { + header = ec.GetHeader(); + + ec.Write(plaintext, 0, plaintext.Length); + ec.FlushFinalBlock(); + } + + ciphertext = encrypted.ToArray(); + } + + using MemoryStream ms = new MemoryStream(); + + Assert.ThrowsException(() => + { + using DecryptionStream dc = + new DecryptionStream(TestData.BytesTestKey, [], header, false, ms, true); + + dc.Write(ciphertext, 0, ciphertext.Length - EncryptedChunkLength); + dc.FlushFinalBlock(); + }); + } } } \ No newline at end of file