[CSHARP-4213] CryptClient.StartDecryptionContext pins buffer but never unpins Created: 15/Jun/22  Updated: 28/Oct/23  Resolved: 17/Jun/22

Status: Closed
Project: C# Driver
Component/s: Field Level Encryption
Affects Version/s: None
Fix Version/s: 2.16.1

Type: Bug Priority: Critical - P2
Reporter: James Kovacs Assignee: Dmitry Lukyanov (Inactive)
Resolution: Fixed Votes: 0
Labels: FLE
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Problem/Incident
Case:

 Description   

The following code is located in CryptClient.cs:

public CryptContext StartDecryptionContext(byte[] buffer)
{
    ContextSafeHandle handle = Library.mongocrypt_ctx_new(_handle);
                                                                                                  
    GCHandle gch = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                                                                                                  
    unsafe
    {
        fixed (byte* p = buffer)
        {
            IntPtr ptr = (IntPtr)p;
            using (PinnedBinary pinned = new PinnedBinary(ptr, (uint)buffer.Length))
            {
                handle.Check(_status, Library.mongocrypt_ctx_decrypt_init(handle, pinned.Handle));
            }
        }
    }
                                                                                                  
    return new CryptContext(handle);
}

Note the 5th line where gch is allocated. The allocated GCHandle is a struct that represents a pointer that can be passed to unmanaged code. You must explicitly free it. It has no finalizer or Dispose method. If you allocate it pinned (as done here), it acts as a rooted reference which will keep the referenced memory (and anything it points to) alive indefinitely.

Note that we later allocate a PinnedBinary, which implements IDisposable and allows us to pin the buffer to be passed to the unmanaged code and correctly unpins it upon return.

Based on my review of the code, the 5th line allocating gch is dead code that should be deleted. Not only is it dead code, it is responsible for keeping alive any System.Byte[] buffer passed into this method. Because we attempt to decrypt any response from the server (in case there are encrypted fields in the response), this results in a slow build-up of System.Byte[] objects when CSFLE is enabled for the .NET/C# Driver.



 Comments   
Comment by Githook User [ 21/Jun/22 ]

Author:

{'name': 'Dmitry Lukyanov', 'email': 'dmitry.lukyanov@mongodb.com', 'username': 'DmitryLukyanov'}

Message: CSHARP-4213: CryptClient.StartDecryptionContext pins buffer but never unpins. (#824)
Branch: v2.16.x
https://github.com/mongodb/mongo-csharp-driver/commit/d2106d681393613ceb4171b4b6c9425d9cc3b006

Comment by Githook User [ 17/Jun/22 ]

Author:

{'name': 'Dmitry Lukyanov', 'email': 'dmitry.lukyanov@mongodb.com', 'username': 'DmitryLukyanov'}

Message: CSHARP-4213: CryptClient.StartDecryptionContext pins buffer but never unpins. (#824)
Branch: master
https://github.com/mongodb/mongo-csharp-driver/commit/887cb0fc4dfae07bee7f2d58bdd87a1146cb019c

Comment by Githook User [ 17/Jun/22 ]

Author:

{'name': 'Dmitry Lukyanov', 'email': 'dmitry.lukyanov@mongodb.com', 'username': 'DmitryLukyanov'}

Message: CSHARP-4213: CryptClient.StartDecryptionContext pins buffer but never unpins. (#372)
Branch: csharp-v1.5.3_release
https://github.com/mongodb/libmongocrypt/commit/34bcccdd8b760235e866193150c9dfb6b2d76778

Comment by Githook User [ 17/Jun/22 ]

Author:

{'name': 'Dmitry Lukyanov', 'email': 'dmitry.lukyanov@mongodb.com', 'username': 'DmitryLukyanov'}

Message: CSHARP-4213: CryptClient.StartDecryptionContext pins buffer but never unpins. (#372)
Branch: master
https://github.com/mongodb/libmongocrypt/commit/a1e585d5c40f8f209b006621f218ada93a6d68c9

Generated at Wed Feb 07 21:47:34 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.