Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix memory leaks and random crashes #178

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 22 additions & 21 deletions LdapForNet/BerConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -502,8 +502,8 @@ private static int BerScanfBitString(BerSafeHandle berElement, char fmt, out obj
byte[] byteArray = null;
var rc = LdapNative.Instance.ber_scanf_bitstring(berElement, new string(fmt, 1), ref ptrResult, ref length);

// try
// {
try
{
if (rc != -1)
{
if (ptrResult != IntPtr.Zero)
Expand All @@ -525,14 +525,14 @@ private static int BerScanfBitString(BerSafeHandle berElement, char fmt, out obj

result = byteArray;
}
// }
// finally
// {
// if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && ptrResult != IntPtr.Zero)
// {
// LdapNative.Instance.ber_memfree(ptrResult);
// }
// }
}
finally
{
if (ptrResult != IntPtr.Zero)
{
LdapNative.Instance.BerScanfFree(fmt, ptrResult);
}
}

return rc;
}
Expand Down Expand Up @@ -582,7 +582,7 @@ private static int BerScanfaString(BerSafeHandle berElement, char fmt, out objec
{
if (result != IntPtr.Zero)
{
LdapNative.Instance.ber_memfree(result);
LdapNative.Instance.BerScanfFree(fmt, result);
}
}

Expand All @@ -591,12 +591,12 @@ private static int BerScanfaString(BerSafeHandle berElement, char fmt, out objec
private static int BerScanfString(BerSafeHandle berElement, char fmt, out object result)
{
int rc;
var ptr = Marshal.AllocHGlobal(IntPtr.Size);
var ptr = IntPtr.Zero;
var length = -1;
result = null;
try
{
rc = LdapNative.Instance.ber_scanf_string(berElement, new string(fmt, 1), ptr, ref length);
rc = LdapNative.Instance.ber_scanf_string(berElement, new string(fmt, 1), ref ptr, ref length);
if (rc != -1)
{
var byteArray = new byte[length];
Expand All @@ -608,7 +608,7 @@ private static int BerScanfString(BerSafeHandle berElement, char fmt, out object
{
if (ptr != IntPtr.Zero)
{
Marshal.FreeHGlobal(ptr);
LdapNative.Instance.BerScanfFree(fmt, ptr);
}
}

Expand Down Expand Up @@ -688,11 +688,11 @@ private static int EncodingByteArrayHelper(BerSafeHandle berElement, byte[] valu

private static int DecodingBerValOstringHelper(BerSafeHandle berElement, char fmt, out byte[] byteArray)
{
var result = Marshal.AllocHGlobal(IntPtr.Size);
var result = IntPtr.Zero;
var binaryValue = new Native.Native.berval();
byteArray = null;

var error = LdapNative.Instance.ber_scanf_ostring(berElement, new string(fmt, 1), result);
var error = LdapNative.Instance.ber_scanf_ostring(berElement, new string(fmt, 1), ref result);

try
{
Expand All @@ -708,7 +708,7 @@ private static int DecodingBerValOstringHelper(BerSafeHandle berElement, char fm
{
if (result != IntPtr.Zero)
{
LdapNative.Instance.ber_memfree(result);
LdapNative.Instance.BerScanfFree(fmt, result);
}
}

Expand Down Expand Up @@ -742,7 +742,7 @@ private static int DecodingBerValByteArrayHelper(BerSafeHandle berElement, char
{
if (result != IntPtr.Zero)
{
LdapNative.Instance.ber_bvfree(result);
LdapNative.Instance.BerScanfFree(fmt, result);
}
}

Expand Down Expand Up @@ -771,6 +771,7 @@ private static int EncodingBerValHelper(BerSafeHandle berElement, byte[] value,
}
return rc;
}

private static int EncodingMultiByteArrayHelper(BerSafeHandle berElement, byte[][] value, char fmt)
{
var stringArray = IntPtr.Zero;
Expand Down Expand Up @@ -947,7 +948,7 @@ private static int DecodingBerValMultiByteArrayHelper(BerSafeHandle berElement,
{
if (ptrResult != IntPtr.Zero)
{
LdapNative.Instance.ber_bvecfree(ptrResult);
LdapNative.Instance.BerScanfFree(fmt, ptrResult);
}
}

Expand Down Expand Up @@ -989,7 +990,7 @@ private static int DecodingBerValMultiByteArrayHelperW(BerSafeHandle berElement,
{
if (ptrResult != IntPtr.Zero)
{
//LdapNative.Instance.ber_bvarrayfree(ptrResult);
LdapNative.Instance.BerScanfFree(fmt, ptrResult);
}
}

Expand Down Expand Up @@ -1019,7 +1020,7 @@ private static int DecodingMultiByteArrayHelper(BerSafeHandle berElement, char f
{
if (ptrResult != IntPtr.Zero)
{
LdapNative.Instance.ber_memfree(ptrResult);
LdapNative.Instance.BerScanfFree(fmt, ptrResult);
}
}

Expand Down
122 changes: 71 additions & 51 deletions LdapForNet/LdapConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,31 +111,41 @@ public async Task BindAsync(Native.Native.LdapAuthType authType, LdapCredential
_native.LdapConnect(_ld, _connectionTimeOut);
var timeout = GetConnectionTimeval();
var result = IntPtr.Zero;
if (authType == Native.Native.LdapAuthType.Simple)
{
result = await _native.BindSimpleAsync(_ld, ldapCredential.UserName, ldapCredential.Password, timeout);
}
else if (authType == Native.Native.LdapAuthType.Anonymous)
{
result = await _native.BindSimpleAsync(_ld, null, null, timeout);
}
else if (authType == Native.Native.LdapAuthType.ExternalAd)
{
// no action required
}
else if (authType != Native.Native.LdapAuthType.Unknown)
try
{
result = await _native.BindSaslAsync(_ld, authType, ldapCredential, timeout);
}
else
{
throw new LdapAuthMethodNotSupportedException(
new LdapExceptionData($"Not implemented mechanism: {authType.ToString()}. Available: {Native.Native.LdapAuthType.Simple.ToString()} | {Native.Native.LdapAuthType.GssApi}. "));
if (authType == Native.Native.LdapAuthType.Simple)
{
result = await _native.BindSimpleAsync(_ld, ldapCredential.UserName, ldapCredential.Password, timeout);
}
else if (authType == Native.Native.LdapAuthType.Anonymous)
{
result = await _native.BindSimpleAsync(_ld, null, null, timeout);
}
else if (authType == Native.Native.LdapAuthType.ExternalAd)
{
// no action required
}
else if (authType != Native.Native.LdapAuthType.Unknown)
{
result = await _native.BindSaslAsync(_ld, authType, ldapCredential, timeout);
}
else
{
throw new LdapAuthMethodNotSupportedException(
new LdapExceptionData($"Not implemented mechanism: {authType.ToString()}. Available: {Native.Native.LdapAuthType.Simple.ToString()} | {Native.Native.LdapAuthType.GssApi}. "));
}

if (result != IntPtr.Zero)
{
ThrowIfParseResultError(result);
}
}

if (result != IntPtr.Zero)
finally
{
ThrowIfParseResultError(result);
if (result != IntPtr.Zero)
{
_native.ldap_msgfree(result);
}
}

_bound = true;
Expand Down Expand Up @@ -347,7 +357,6 @@ private DirectoryResponse ProcessResponse(DirectoryRequest directoryRequest,
CancellationToken token)
{
var status = LdapResultCompleteStatus.Unknown;
var msg = Marshal.AllocHGlobal(IntPtr.Size);

var timeout = GetConnectionTimeval();

Expand All @@ -357,39 +366,50 @@ private DirectoryResponse ProcessResponse(DirectoryRequest directoryRequest,
DirectoryResponse response = default;
while (status != LdapResultCompleteStatus.Complete && !token.IsCancellationRequested)
{
var msg = IntPtr.Zero;
var resType = _native.ldap_result(_ld, messageId, 0, timeout, ref msg);
ThrowIfResultError(directoryRequest, resType, response);

status = requestHandler.Handle(_ld, resType, msg, out response);
response.MessageId = messageId;

if (status == LdapResultCompleteStatus.Unknown)
try
{
throw new LdapException(new LdapExceptionData($"Unknown search type {resType}", nameof(_native.ldap_result), 1){ Response = response});
ThrowIfResultError(directoryRequest, resType, response);

status = requestHandler.Handle(_ld, resType, msg, out response);
response.MessageId = messageId;

if (status == LdapResultCompleteStatus.Unknown)
{
throw new LdapException(new LdapExceptionData($"Unknown search type {resType}", nameof(_native.ldap_result), 1){ Response = response});
}

if (status == LdapResultCompleteStatus.Complete)
{
var responseReferral = new Uri[0];
var responseControl = new DirectoryControl[0];
var res = ParseResultError(msg, out var errorMessage, out var matchedDn, ref responseReferral, ref responseControl);

if (res == (int)Native.Native.ResultCode.SizeLimitExceeded
&& directoryRequest is SearchRequest searchRequest
&& response is SearchResponse searchResponse
&& searchRequest.SizeLimit != 0
&& searchResponse.Entries.Count >= searchRequest.SizeLimit)
{
Debug.WriteLine("ldap_parse_result returned ResultCode.SizeLimitExceeded but the correct number of entries were already returned");
res = (int)Native.Native.ResultCode.Success;
errorMessage = null;
}

response.ResultCode = (Native.Native.ResultCode)res;
response.ErrorMessage = errorMessage;
response.Referral = responseReferral;
response.Controls = responseControl;
response.MatchedDN = matchedDn;
}
}

if (status == LdapResultCompleteStatus.Complete)
finally
{
var responseReferral = new Uri[0];
var responseControl = new DirectoryControl[0];
var res = ParseResultError(msg, out var errorMessage, out var matchedDn, ref responseReferral, ref responseControl);

if (res == (int)Native.Native.ResultCode.SizeLimitExceeded
&& directoryRequest is SearchRequest searchRequest
&& response is SearchResponse searchResponse
&& searchRequest.SizeLimit != 0
&& searchResponse.Entries.Count >= searchRequest.SizeLimit)
if (msg != IntPtr.Zero)
{
Debug.WriteLine("ldap_parse_result returned ResultCode.SizeLimitExceeded but the correct number of entries were already returned");
res = (int)Native.Native.ResultCode.Success;
errorMessage = null;
_native.ldap_msgfree(msg);
}

response.ResultCode = (Native.Native.ResultCode)res;
response.ErrorMessage = errorMessage;
response.Referral = responseReferral;
response.Controls = responseControl;
response.MatchedDN = matchedDn;
}
}

Expand Down Expand Up @@ -457,7 +477,7 @@ private int ParseResultError(IntPtr msg, out string errorMessage, out string mat
var referrals = IntPtr.Zero;
var serverctrls = IntPtr.Zero;
_native.ThrowIfError(_ld, _native.ldap_parse_result(_ld, msg, ref rc, ref matchedDnPtr, ref errorMessagePtr,
ref referrals, ref serverctrls, 1), nameof(_native.ldap_parse_result));
ref referrals, ref serverctrls, 0), nameof(_native.ldap_parse_result));
errorMessage = Encoder.Instance.PtrToString(errorMessagePtr);
matchedDn = Encoder.Instance.PtrToString(matchedDnPtr);
if (referrals != IntPtr.Zero)
Expand Down
9 changes: 6 additions & 3 deletions LdapForNet/Native/LdapNative.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ internal abstract int Search(SafeHandle ld, string @base, int scope, string filt
internal abstract LdapResultType ldap_result(SafeHandle ld, int msgid, int all, LDAP_TIMEVAL timeout,
ref IntPtr pMessage);


internal abstract int ldap_parse_result(SafeHandle ld, IntPtr result, ref int errcodep, ref IntPtr matcheddnp,
ref IntPtr errmsgp, ref IntPtr referralsp, ref IntPtr serverctrlsp, int freeit);

Expand Down Expand Up @@ -140,19 +141,21 @@ internal abstract int ldap_start_tls_s(SafeHandle ld, ref int serverReturnValue,
internal abstract int ber_peek_tag(SafeHandle berElement, ref int length);

internal abstract int ber_scanf_ptr(SafeHandle berElement, string format, ref IntPtr value);
internal abstract int ber_scanf_ostring(SafeHandle berElement, string format, IntPtr value);
internal abstract int ber_scanf_ostring(SafeHandle berElement, string format, ref IntPtr value);

internal abstract int ber_scanf_string(SafeHandle berElement, string format, IntPtr value, ref int length);
internal abstract int ber_scanf_string(SafeHandle berElement, string format, ref IntPtr value, ref int length);
internal abstract int ber_scanf_bitstring(SafeHandle berElement, string format, ref IntPtr value, ref int length);

internal abstract int ber_bvfree(IntPtr value);

internal abstract int ber_bvecfree(IntPtr value);

internal abstract IntPtr ber_free([In] IntPtr berelement, int option);
internal abstract IntPtr ber_free(IntPtr berelement, int option);
internal abstract void ber_memfree(IntPtr value);

internal abstract bool BerScanfSupports(char fmt);

internal abstract void BerScanfFree(char fmt, IntPtr ptr);

internal void ThrowIfError(int res, string method, IDictionary<string,string> details = default)
{
Expand Down
Loading