Skip to content

Commit aee2420

Browse files
author
dahall
committed
Fixed enumerators that don't capture native memory #569
1 parent f25a70b commit aee2420

File tree

11 files changed

+72
-12
lines changed

11 files changed

+72
-12
lines changed

Core/Collections/NativeMemoryEnumerator.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ public class NativeMemoryEnumerator<T> : UntypedNativeMemoryEnumerator, IEnumera
1818
/// <exception cref="InsufficientMemoryException"></exception>
1919
public NativeMemoryEnumerator(IntPtr ptr, int length, int prefixBytes = 0, SizeT allocatedBytes = default) : base(ptr, typeof(T), length, prefixBytes, allocatedBytes) { }
2020

21+
/// <summary>Initializes a new instance of the <see cref="NativeMemoryEnumerator{T}"/> class.</summary>
22+
/// <param name="ptr">A pointer to the starting address of a specified number of <typeparamref name="T"/> elements in memory.</param>
23+
/// <param name="length">The number of <typeparamref name="T"/> elements to be included in the enumeration.</param>
24+
/// <param name="prefixBytes">Bytes to skip before reading the first element.</param>
25+
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
26+
/// <exception cref="ArgumentOutOfRangeException">count</exception>
27+
/// <exception cref="InsufficientMemoryException"></exception>
28+
public NativeMemoryEnumerator(SafeHandle ptr, int length, int prefixBytes = 0, SizeT allocatedBytes = default) : base(ptr, typeof(T), length, prefixBytes, allocatedBytes) { }
29+
2130
/// <summary>Gets the element in the collection at the current position of the enumerator.</summary>
2231
public new T? Current => (T?)base.Current;
2332

@@ -46,6 +55,9 @@ public class UntypedNativeMemoryEnumerator : IEnumerator, IEnumerable
4655
/// <summary>A pointer to the starting address of a specified number of <see cref="type"/> elements in memory.</summary>
4756
protected IntPtr ptr;
4857

58+
/// <summary>A safe handle to the starting address of a specified number of <see cref="type"/> elements in memory.</summary>
59+
protected SafeHandle? handle;
60+
4961
/// <summary>The size of <see cref="type"/>.</summary>
5062
protected SizeT stSize;
5163

@@ -77,6 +89,32 @@ public UntypedNativeMemoryEnumerator(IntPtr ptr, Type type, int length, int pref
7789
throw new InsufficientMemoryException();
7890
}
7991

92+
/// <summary>Initializes a new instance of the <see cref="NativeMemoryEnumerator{T}"/> class.</summary>
93+
/// <param name="ptr">A pointer to the starting address of a specified number of <paramref name="type"/> elements in memory.</param>
94+
/// <param name="type">The type of the element to extract from memory.</param>
95+
/// <param name="length">The number of <paramref name="type"/> elements to be included in the enumeration.</param>
96+
/// <param name="prefixBytes">Bytes to skip before reading the first element.</param>
97+
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
98+
/// <exception cref="ArgumentOutOfRangeException">count</exception>
99+
/// <exception cref="ArgumentNullException">type</exception>
100+
/// <exception cref="InsufficientMemoryException"></exception>
101+
public UntypedNativeMemoryEnumerator(SafeHandle ptr, Type type, int length, int prefixBytes = 0, SizeT allocatedBytes = default)
102+
{
103+
if (length < 0 || ptr.IsInvalid && length != 0)
104+
throw new ArgumentOutOfRangeException(nameof(length));
105+
if (type is null)
106+
throw new ArgumentNullException(nameof(type));
107+
this.type = type;
108+
this.ptr = ptr.DangerousGetHandle();
109+
handle = ptr;
110+
count = length;
111+
prefix = prefixBytes;
112+
allocated = allocatedBytes == default ? (SizeT)uint.MaxValue : allocatedBytes;
113+
stSize = InteropExtensions.SizeOf(type);
114+
if (allocatedBytes > 0 && stSize * length + prefixBytes > allocatedBytes)
115+
throw new InsufficientMemoryException();
116+
}
117+
80118
/// <summary>Gets the element in the collection at the current position of the enumerator.</summary>
81119
public virtual object? Current => index >= 0 && index < count
82120
? ptr.ToStructure(type, allocated, prefix + index * stSize, out _)

Core/Extensions/InteropExtensions.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,28 @@ public static IntPtr StructureToPtr<T>(this T value, Func<int, IntPtr> memAlloc,
678678
public static System.Collections.IEnumerable ToIEnum(this IntPtr ptr, Type type, SizeT count, SizeT prefixBytes = default, SizeT allocatedBytes = default) =>
679679
new UntypedNativeMemoryEnumerator(ptr, type, count, prefixBytes, allocatedBytes);
680680

681+
/// <summary>Converts an <see cref="SafeHandle"/> that points to a C-style array into an <see cref="IEnumerable{T}"/>.</summary>
682+
/// <typeparam name="T">Type of native structure used by the C-style array.</typeparam>
683+
/// <param name="ptr">The <see cref="SafeHandle"/> pointing to the native array.</param>
684+
/// <param name="count">The number of items in the native array.</param>
685+
/// <param name="prefixBytes">Bytes to skip before reading the array.</param>
686+
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
687+
/// <returns>An <see cref="IEnumerable{T}"/> exposing the elements of the native array.</returns>
688+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
689+
public static IEnumerable<T?> ToIEnum<T>(this SafeHandle ptr, SizeT count, SizeT prefixBytes = default, SizeT allocatedBytes = default) =>
690+
new NativeMemoryEnumerator<T>(ptr, count, prefixBytes, allocatedBytes);
691+
692+
/// <summary>Converts an <see cref="SafeHandle"/> that points to a C-style array into an <see cref="IEnumerable{T}"/>.</summary>
693+
/// <param name="ptr">The <see cref="SafeHandle"/> pointing to the native array.</param>
694+
/// <param name="type">Type of native structure used by the C-style array.</param>
695+
/// <param name="count">The number of items in the native array.</param>
696+
/// <param name="prefixBytes">Bytes to skip before reading the array.</param>
697+
/// <param name="allocatedBytes">If known, the total number of bytes allocated to the native memory in <paramref name="ptr"/>.</param>
698+
/// <returns>An <see cref="IEnumerable{T}"/> exposing the elements of the native array.</returns>
699+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
700+
public static System.Collections.IEnumerable ToIEnum(this SafeHandle ptr, Type type, SizeT count, SizeT prefixBytes = default, SizeT allocatedBytes = default) =>
701+
new UntypedNativeMemoryEnumerator(ptr, type, count, prefixBytes, allocatedBytes);
702+
681703
/// <summary>Converts a <see cref="SecureString"/> to a string.</summary>
682704
/// <param name="s">The <see cref="SecureString"/> value.</param>
683705
/// <returns>The extracted string.</returns>

Core/InteropServices/SafeMemoryHandle.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@ public IEnumerable<T> ToEnumerable<T>(SizeT count, SizeT prefixBytes = default)
977977
try
978978
{
979979
Lock();
980-
foreach (T? i in handle.ToIEnum<T>(count, prefixBytes, sz))
980+
foreach (T? i in this.ToIEnum<T>(count, prefixBytes, sz))
981981
yield return i!;
982982
}
983983
finally

PInvoke/FwpUClnt/FwpMU.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ private SafeFwpmArray() : base(IntPtr.Zero, true) { }
580580
/// <inheritdoc/>
581581
protected override bool InternalReleaseHandle() { FwpmFreeMemory0(ref handle); handle = default; return true; }
582582

583-
private IEnumerable<T> Enumerate() => byRef ? handle.ToIEnum<IntPtr>(Count).Select(p => p.Convert<T>(uint.MaxValue, CharSet.Unicode)) : handle.ToIEnum<T>(Count);
583+
private IEnumerable<T> Enumerate() => byRef ? this.ToIEnum<IntPtr>(Count).Select(p => p.Convert<T>(uint.MaxValue, CharSet.Unicode)) : this.ToIEnum<T>(Count);
584584
}
585585

586586
/// <summary>Provides a <see cref="SafeHandle"/> for memory returned by FWP functions that is disposed using <see cref="FwpmFreeMemory0"/>.</summary>
@@ -600,7 +600,7 @@ public partial class SafeFwpmMem
600600
/// of elements.
601601
/// </param>
602602
/// <returns>An array of type <typeparamref name="T"/> of length <paramref name="elemCount"/>.</returns>
603-
public T[] ToArray<T>(SizeT elemCount, bool byRef) => byRef ? Array.ConvertAll(handle.ToArray<IntPtr>(elemCount) ?? new IntPtr[0], p => p.Convert<T>(uint.MaxValue, CharSet.Unicode)!) : handle.ToArray<T>(elemCount) ?? new T[0];
603+
public T[] ToArray<T>(SizeT elemCount, bool byRef) => byRef ? Array.ConvertAll(handle.ToArray<IntPtr>(elemCount) ?? [], p => p.Convert<T>(uint.MaxValue, CharSet.Unicode)!) : handle.ToArray<T>(elemCount) ?? [];
604604

605605
/// <summary>Extracts a structure from this memory.</summary>
606606
/// <typeparam name="T">The type of the structure to extract.</typeparam>

PInvoke/NetApi32/LmApiBuf.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public static SafeNetApiBuffer Allocate(uint size)
115115
/// <typeparam name="T">The type of the structure.</typeparam>
116116
/// <param name="count">The count of structures in the list.</param>
117117
/// <returns>The list of structures.</returns>
118-
public IEnumerable<T?> ToIEnum<T>(int count) => handle.ToIEnum<T>(count);
118+
public IEnumerable<T?> ToIEnum<T>(int count) => this.ToIEnum<T>(count);
119119

120120
/// <inheritdoc/>
121121
public override string? ToString() => StringHelper.GetString(handle);

PInvoke/Security/AdvApi32/NTSecApi.Audit.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1021,7 +1021,7 @@ public IEnumerable<T> ToIEnum<T>(int count, int prefixBytes = 0) where T : struc
10211021
{
10221022
if (IsInvalid) return Enumerable.Empty<T>();
10231023
if (!typeof(T).IsBlittable()) throw new ArgumentException(@"Structure layout is not sequential or explicit.");
1024-
return handle.ToIEnum<T>(count, prefixBytes);
1024+
return this.ToIEnum<T>(count, prefixBytes);
10251025
}
10261026

10271027
/// <summary>

PInvoke/Security/AdvApi32/NTSecApi.Lsa.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ public static IEnumerable<string> LsaEnumerateAccountRights(LSA_HANDLE PolicyHan
461461
var winErr = LsaNtStatusToWinError(ret);
462462
if (winErr == Win32Error.ERROR_FILE_NOT_FOUND) return new string[0];
463463
winErr.ThrowIfFailed();
464-
return mem.DangerousGetHandle().ToIEnum<LSA_UNICODE_STRING>((int)cnt).Select(u => (string)u.ToString().Clone()).ToArray();
464+
return mem.ToIEnum<LSA_UNICODE_STRING>((int)cnt).Select(u => (string)u.ToString().Clone()).ToArray();
465465
}
466466

467467
/// <summary>
@@ -552,7 +552,7 @@ public static IEnumerable<PSID> LsaEnumerateAccountsWithUserRight(LSA_HANDLE Pol
552552
var ret = LsaEnumerateAccountsWithUserRight(PolicyHandle, UserRights, out var mem, out var cnt);
553553
if (ret == NTStatus.STATUS_NO_MORE_ENTRIES) return new PSID[0];
554554
LsaNtStatusToWinError(ret).ThrowIfFailed();
555-
return mem.DangerousGetHandle().ToIEnum<LSA_ENUMERATION_INFORMATION>(cnt).Select(u => u.Sid).ToArray();
555+
return mem.ToIEnum<LSA_ENUMERATION_INFORMATION>(cnt).Select(u => u.Sid).ToArray();
556556
}
557557

558558
/// <summary>

PInvoke/Security/AdvApi32/PSID.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ public SafePSIDArray(IntPtr preexistingHandle, int count, bool ownsHandle = true
306306
if (ownsHandle)
307307
Count = count;
308308
else
309-
items = [.. handle.ToIEnum<IntPtr>(count).Select(p => new SafePSID((PSID)p))];
309+
items = [.. this.ToIEnum<IntPtr>(count).Select(p => new SafePSID((PSID)p))];
310310
}
311311

312312
/// <summary>Initializes a new instance of the <see cref="SafePSIDArray"/> class.</summary>

PInvoke/Security/AdvApi32/WinCred.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1917,6 +1917,6 @@ public SafeCredMemoryHandle() : this(IntPtr.Zero) { }
19171917
/// <exception cref="InsufficientMemoryException"></exception>
19181918
public T ToStructure<T>() where T : struct => handle.ToStructure<T>();
19191919

1920-
internal CREDENTIAL_MGD[] GetCredArray(int count) => handle.ToIEnum<IntPtr>(count).Select(p => new CREDENTIAL_MGD(p.ToStructure<CREDENTIAL>())).ToArray();
1920+
internal CREDENTIAL_MGD[] GetCredArray(int count) => this.ToIEnum<IntPtr>(count).Select(p => new CREDENTIAL_MGD(p.ToStructure<CREDENTIAL>())).ToArray();
19211921
}
19221922
}

PInvoke/Shared/InteropServices/SafeNativeArray.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ protected void CopyTo(IntPtr ptr, int start = 0, int length = -1)
277277

278278
/// <summary>Enumerates the elements.</summary>
279279
/// <returns>An enumeration of values from the pointer.</returns>
280-
protected IEnumerable<TElem> EnumElements() => handle.ToIEnum<TElem>(Count, (int)HeaderSize);
280+
protected IEnumerable<TElem> EnumElements() => this.ToIEnum<TElem>(Count, (int)HeaderSize);
281281

282282
private static int GetElemCountFromBytes(uint byteSize, uint headerSize)
283283
{

0 commit comments

Comments
 (0)