/// <summary> /// Convert each Oid's value to an ASCII string, then create an unmanaged array of "numOids" LPSTR pointers, one for each Oid. /// "numOids" is the number of LPSTR pointers. This is normally the same as oids.Count, except in the case where a malicious caller /// appends to the OidCollection while this method is in progress. In such a case, this method guarantees only that this won't create /// an unmanaged buffer overflow condition. /// </summary> public static SafeHandle ToLpstrArray(this OidCollection?oids, out int numOids) { if (oids == null || oids.Count == 0) { numOids = 0; return(SafeLocalAllocHandle.InvalidHandle); } // Copy the oid strings to a local array to prevent a security race condition where // the OidCollection or individual oids can be modified by another thread and // potentially cause a buffer overflow var oidStrings = new string[oids.Count]; for (int i = 0; i < oidStrings.Length; i++) { oidStrings[i] = oids[i].Value !; } unsafe { int allocationSize = checked (oidStrings.Length * sizeof(void *)); foreach (string oidString in oidStrings) { checked { allocationSize += oidString.Length + 1; // Encoding.ASCII doesn't have a fallback, so it's fine to use String.Length } } SafeLocalAllocHandle safeLocalAllocHandle = SafeLocalAllocHandle.Create(allocationSize); byte **pOidPointers = (byte **)(safeLocalAllocHandle.DangerousGetHandle()); byte * pOidContents = (byte *)(pOidPointers + oidStrings.Length); for (int i = 0; i < oidStrings.Length; i++) { string oidString = oidStrings[i]; pOidPointers[i] = pOidContents; int bytesWritten = Encoding.ASCII.GetBytes(oidString, new Span <byte>(pOidContents, oidString.Length)); Debug.Assert(bytesWritten == oidString.Length); pOidContents[oidString.Length] = 0; pOidContents += oidString.Length + 1; } numOids = oidStrings.Length; return(safeLocalAllocHandle); } }