[API Proposal]: Batch #1 of caller-unsafe APIs
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
### Background and motivation
https://github.com/dotnet/runtime/issues/125145
### API Proposal
```diff
namespace System;
public static class GC
{
+ /// Returns a T[] whose elements are not zero-initialized, so reads observe leftover, uninitialized heap bytes.
+ unsafe
public static T[] AllocateUninitializedArray(int length, bool pinned = false);
}
```
```diff
namespace System.Runtime.CompilerServices;
public static class RuntimeHelpers
{
+ /// Reads SizeOf(type) bytes from a caller-supplied reference and boxes them as that type; the bytes may be out of range, uninitialized, or contain a fabricated reference field.
+ unsafe
public static object? Box(ref byte target, System.RuntimeTypeHandle type);
}
```
```diff
namespace System.Security.Cryptography;
public sealed partial class DSAOpenSsl : System.Security.Cryptography.DSA
{
+ /// Wraps a raw, caller-supplied EVP_PKEY* pointer that the crypto stack later dereferences.
+ unsafe
public DSAOpenSsl(System.IntPtr handle);
}
```
```diff
namespace System.Security.Cryptography;
public sealed partial class ECDiffieHellmanOpenSsl : System.Security.Cryptography.ECDiffieHellman
{
+ /// Wraps a raw, caller-supplied EVP_PKEY* pointer that the crypto stack later dereferences.
+ unsafe
public ECDiffieHellmanOpenSsl(System.IntPtr handle);
}
```
```diff
namespace System.Security.Cryptography;
public sealed partial class ECDsaOpenSsl : System.Security.Cryptography.ECDsa
{
+ /// Wraps a raw, caller-supplied EVP_PKEY* pointer that the crypto stack later dereferences.
+ unsafe
public ECDsaOpenSsl(System.IntPtr handle);
}
```
```diff
namespace System.Security.Cryptography;
public sealed partial class RSAOpenSsl : System.Security.Cryptography.RSA
{
+ /// Wraps a raw, caller-supplied EVP_PKEY* pointer that the crypto stack later dereferences.
+ unsafe
public RSAOpenSsl(System.IntPtr handle);
}
```
```diff
namespace System.Security.Cryptography.X509Certificates;
public partial class X509Certificate : System.IDisposable, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable
{
+ /// Wraps a raw, caller-supplied PCCERT_CONTEXT pointer that the certificate stack later dereferences.
+ unsafe
public X509Certificate(System.IntPtr handle);
}
```
```diff
namespace System.Security.Cryptography.X509Certificates;
public partial class X509Certificate2 : System.Security.Cryptography.X509Certificates.X509Certificate
{
+ /// Wraps a raw, caller-supplied PCCERT_CONTEXT pointer that the certificate stack later dereferences.
+ unsafe
public X509Certificate2(System.IntPtr handle);
}
```
```diff
namespace System.Security.Cryptography.X509Certificates;
public partial class X509Chain : System.IDisposable
{
+ /// Wraps a raw, caller-supplied PCCERT_CHAIN_CONTEXT pointer that the chain engine later dereferences.
+ unsafe
public X509Chain(System.IntPtr chainContext);
}
```
```diff
namespace System.Security.Cryptography.X509Certificates;
public sealed partial class X509Store : System.IDisposable
{
+ /// Wraps a raw, caller-supplied HCERTSTORE handle (a pointer to a native store) that the store stack later dereferences.
+ unsafe
public X509Store(System.IntPtr storeHandle);
}
```
```diff
namespace System.Diagnostics.SymbolStore;
public partial interface ISymbolBinder1
{
+ /// Dereferences a raw, caller-supplied native/COM importer interface pointer.
+ unsafe
System.Diagnostics.SymbolStore.ISymbolReader? GetReader(System.IntPtr importer, string filename, string searchPath);
}
```
```diff
namespace System.Diagnostics.SymbolStore;
public partial interface ISymbolWriter
{
+ /// Dereferences a raw, caller-supplied native/COM emitter interface pointer.
+ unsafe
void Initialize(System.IntPtr emitter, string filename, bool fFullBuild);
+ /// Dereferences a raw, caller-supplied native/COM writer interface pointer.
+ unsafe
void SetUnderlyingWriter(System.IntPtr underlyingWriter);
}
```
```diff
namespace System.Transactions;
public partial interface IDtcTransaction
{
+ /// Reads the abort-reason structure through a raw, caller-supplied IntPtr.
+ unsafe
void Abort(System.IntPtr reason, int retaining, int async);
+ /// Writes XACTTRANSINFO through a raw, caller-supplied IntPtr.
+ unsafe
void GetTransactionInfo(System.IntPtr transactionInformation);
}
```
```diff
namespace System.Security.Principal;
public sealed partial class SecurityIdentifier : System.Security.Principal.IdentityReference, System.IComparable
{
+ /// Reads the binary SID through a raw, caller-supplied address.
+ unsafe
public SecurityIdentifier(System.IntPtr binaryForm);
}
```
```diff
namespace System;
public struct RuntimeTypeHandle
{
+ /// Builds a handle from a raw, unvalidated MethodTable* pointer that the runtime later dereferences.
+ unsafe
public static RuntimeTypeHandle FromIntPtr(System.IntPtr value);
}
```
```diff
namespace System;
public struct RuntimeMethodHandle
{
+ /// Builds a handle from a raw, unvalidated MethodDesc* pointer that the runtime later dereferences.
+ unsafe
public static RuntimeMethodHandle FromIntPtr(System.IntPtr value);
}
```
```diff
namespace System;
public struct RuntimeFieldHandle
{
+ /// Builds a handle from a raw, unvalidated FieldDesc* pointer that the runtime later dereferences.
+ unsafe
public static RuntimeFieldHandle FromIntPtr(System.IntPtr value);
}
```
```diff
namespace System.Security.Cryptography;
public sealed partial class SafeEvpPKeyHandle : System.Runtime.InteropServices.SafeHandle
{
+ /// Wraps a raw, caller-supplied EVP_PKEY* pointer that the crypto stack later dereferences.
+ unsafe
public SafeEvpPKeyHandle(System.IntPtr handle, bool ownsHandle);
}
```
```diff
namespace System.Buffers;
public abstract partial class ArrayPool
{
+ /// Returns an array whose elements may be uninitialized — the shared pool allocates primitive-typed buffers via GC.AllocateUninitializedArray, so reads before writing observe leftover, undefined heap bytes.
+ unsafe
public abstract T[] Rent(int minimumLength);
}
```
```diff
namespace System.Buffers;
public abstract partial class MemoryPool : System.IDisposable
{
+ /// Returns an IMemoryOwner whose Memory may be uninitialized — the shared pool rents from the shared ArrayPool (GC.AllocateUninitializedArray) and exposes the full rented buffer, so reads before writing observe leftover, undefined heap bytes.
+ unsafe
public abstract System.Buffers.IMemoryOwner Rent(int minBufferSize = -1);
}
```
```diff
namespace Microsoft.Win32.SafeHandles;
public sealed partial class SafeAccessTokenHandle : System.Runtime.InteropServices.SafeHandle
{
+ /// Wraps a raw, caller-supplied OS handle; the safe Dispose/finalizer later closes it, so a bogus or non-owned handle can close an unrelated handle and corrupt process state.
+ unsafe
public SafeAccessTokenHandle(System.IntPtr handle);
}
```
```diff
namespace Microsoft.Win32.SafeHandles;
public sealed partial class SafeFileHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
{
+ /// Wraps a raw, caller-supplied OS handle; the safe Dispose/finalizer later closes it, so a bogus or non-owned handle can close an unrelated handle and corrupt process state.
+ unsafe
public SafeFileHandle(System.IntPtr preexistingHandle, bool ownsHandle);
}
```
```diff
namespace Microsoft.Win32.SafeHandles;
public sealed partial class SafeNCryptKeyHandle : Microsoft.Win32.SafeHandles.SafeNCryptHandle
{
+ /// Wraps a raw, caller-supplied OS handle; the safe Dispose/finalizer later closes it, so a bogus or non-owned handle can close an unrelated handle and corrupt process state.
+ unsafe
public SafeNCryptKeyHandle(System.IntPtr handle, System.Runtime.InteropServices.SafeHandle parentHandle);
}
```
```diff
namespace Microsoft.Win32.SafeHandles;
public sealed partial class SafePipeHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
{
+ /// Wraps a raw, caller-supplied OS handle; the safe Dispose/finalizer later closes it, so a bogus or non-owned handle can close an unrelated handle and corrupt process state.
+ unsafe
public SafePipeHandle(System.IntPtr preexistingHandle, bool ownsHandle);
}
```
```diff
namespace Microsoft.Win32.SafeHandles;
public sealed partial class SafeProcessHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
{
+ /// Wraps a raw, caller-supplied OS handle; the safe Dispose/finalizer later closes it, so a bogus or non-owned handle can close an unrelated handle and corrupt process state.
+ unsafe
public SafeProcessHandle(System.IntPtr existingHandle, bool ownsHandle);
}
```
```diff
namespace Microsoft.Win32.SafeHandles;
public sealed partial class SafeRegistryHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
{
+ /// Wraps a raw, caller-supplied OS handle; the safe Dispose/finalizer later closes it, so a bogus or non-owned handle can close an unrelated handle and corrupt process state.
+ unsafe
public SafeRegistryHandle(System.IntPtr preexistingHandle, bool ownsHandle);
}
```
```diff
namespace Microsoft.Win32.SafeHandles;
public sealed partial class SafeWaitHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
{
+ /// Wraps a raw, caller-supplied OS handle; the safe Dispose/finalizer later closes it, so a bogus or non-owned handle can close an unrelated handle and corrupt process state.
+ unsafe
public SafeWaitHandle(System.IntPtr existingHandle, bool ownsHandle);
}
```
```diff
namespace System.Net.Sockets;
public sealed partial class SafeSocketHandle : Microsoft.Win32.SafeHandles.SafeHandleMinusOneIsInvalid
{
+ /// Wraps a raw, caller-supplied OS handle; the safe Dispose/finalizer later closes it, so a bogus or non-owned handle can close an unrelated handle and corrupt process state.
+ unsafe
public SafeSocketHandle(nint preexistingHandle, bool ownsHandle);
}
```
### API Usage
The annotated APIs will require `unsafe { }` context at their call site once the language-level `unsafe` requirement is enforced.
### Alternative Designs
_No response_
### Risks
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.