dotnet / dotnet/runtime

[API Proposal]: Batch #1 of caller-unsafe APIs

Open
#129,751 16 comments 0 reactions 1 assignee Claimed by @EgorBo View on GitHub
api-approved area-Meta reduce-unsafe
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.