dotnet / dotnet/runtime

[API Proposal]: Mark Unsafe as caller-unsafe

Open
#126,956 23 comments 2 reactions 1 assignee Claimed by @EgorBo View on GitHub
api-approved area-System.Runtime.CompilerServices 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
public static partial class Unsafe
{
+ /// Applies an unchecked byte offset to a managed reference; the result may point outside the source object and can crash the GC even if it is never dereferenced.
+ unsafe
public static ref T AddByteOffset(ref T source, System.IntPtr byteOffset) where T : allows ref struct { throw null; }

+ /// Applies an unchecked byte offset to a managed reference; the result may point outside the source object and can crash the GC even if it is never dereferenced.
+ unsafe
public static ref T AddByteOffset(ref T source, nuint byteOffset) where T : allows ref struct { throw null; }

+ /// Applies an unchecked element offset to a managed reference; the result may point outside the source object and can crash the GC even if it is never dereferenced.
+ unsafe
public static ref T Add(ref T source, int elementOffset) where T : allows ref struct { throw null; }

+ /// Applies an unchecked element offset to a managed reference; the result may point outside the source object and can crash the GC even if it is never dereferenced.
+ unsafe
public static ref T Add(ref T source, System.IntPtr elementOffset) where T : allows ref struct { throw null; }

+ /// Applies an unchecked element offset to a managed reference; the result may point outside the source object and can crash the GC even if it is never dereferenced.
+ unsafe
public static ref T Add(ref T source, nuint elementOffset) where T : allows ref struct { throw null; }

public static bool AreSame([System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T left, [System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T right) where T : allows ref struct { throw null; }

+ /// Strips read-only protection from a reference, yielding a writable reference; writing through it to genuinely read-only memory (e.g. an RVA static field or a string literal) can corrupt memory or fault.
+ unsafe
public static ref T AsRef(scoped ref readonly T source) where T : allows ref struct { throw null; }

+ /// Reinterprets an object reference as an unrelated type with no type check; later field access can read past the object's bounds or treat arbitrary bytes as a reference.
+ unsafe
public static T? As(object? o) where T : class? { throw null; }

+ ///
+ /// Reinterprets a reference to one type as a reference to another with no type or size check;
+ /// accesses may exceed the source's bounds or fabricate references from arbitrary bytes.
+ ///
+ unsafe
public static ref TTo As(ref TFrom source) where TFrom : allows ref struct where TTo : allows ref struct { throw null; }

+ ///
+ /// Reinterprets the bits of a value as another type of the same size; may expose the source's
+ /// padding bytes or fabricate a reference/pointer field in the result from arbitrary bits.
+ ///
+ unsafe
public static TTo BitCast(TFrom source) where TFrom : allows ref struct where TTo : allows ref struct { throw null; }

public static System.IntPtr ByteOffset([System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T origin, [System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T target) where T : allows ref struct { throw null; }

+ /// Copies a caller-specified number of bytes between two references with no bounds check; may read or write past the end of either buffer.
+ unsafe
public static void CopyBlock(ref byte destination, ref readonly byte source, uint byteCount) { }

+ ///
+ /// Copies a caller-specified number of bytes between two references with no bounds check;
+ /// may read or write past the end of either buffer.
+ ///
+ unsafe
public static void CopyBlockUnaligned(ref byte destination, ref readonly byte source, uint byteCount) { }

+ /// Writes a caller-specified number of bytes through a reference with no bounds check; may write past the end of the buffer.
+ unsafe
public static void InitBlock(ref byte startAddress, byte value, uint byteCount) { }

+ /// Writes a caller-specified number of bytes through a reference with no bounds or alignment check; may write past the end of the buffer.
+ unsafe
public static void InitBlockUnaligned(ref byte startAddress, byte value, uint byteCount) { }

public static bool IsAddressGreaterThan([System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T left, [System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T right) where T : allows ref struct { throw null; }

public static bool IsAddressGreaterThanOrEqualTo([System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T left, [System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T right) where T : allows ref struct { throw null; }

public static bool IsAddressLessThan([System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T left, [System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T right) where T : allows ref struct { throw null; }

public static bool IsAddressLessThanOrEqualTo([System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T left, [System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T right) where T : allows ref struct { throw null; }

public static bool IsNullRef(ref readonly T source) where T : allows ref struct { throw null; }

public static ref T NullRef() where T : allows ref struct { throw null; }

+ /// Reads a value of type T from a byte reference with no bounds or alignment check; may read past the buffer and can populate a pointer/reference field of T from arbitrary bytes.
+ unsafe
public static T ReadUnaligned(scoped ref readonly byte source) where T : allows ref struct { throw null; }

public static int SizeOf() where T : allows ref struct { throw null; }

+ /// Leaves the variable uninitialized, so subsequent reads observe arbitrary, uninitialized memory.
+ unsafe
public static void SkipInit(out T value) where T : allows ref struct { throw null; }

+ /// Applies an unchecked byte offset to a managed reference; the result may point outside the source object and can crash the GC even if it is never dereferenced.
+ unsafe
public static ref T SubtractByteOffset(ref T source, System.IntPtr byteOffset) where T : allows ref struct { throw null; }

+ /// Applies an unchecked byte offset to a managed reference; the result may point outside the source object and can crash the GC even if it is never dereferenced.
+ unsafe
public static ref T SubtractByteOffset(ref T source, nuint byteOffset) where T : allows ref struct { throw null; }

+ /// Applies an unchecked element offset to a managed reference; the result may point outside the source object and can crash the GC even if it is never dereferenced.
+ unsafe
public static ref T Subtract(ref T source, int elementOffset) where T : allows ref struct { throw null; }

+ /// Applies an unchecked element offset to a managed reference; the result may point outside the source object and can crash the GC even if it is never dereferenced.
+ unsafe
public static ref T Subtract(ref T source, System.IntPtr elementOffset) where T : allows ref struct { throw null; }

+ /// Applies an unchecked element offset to a managed reference; the result may point outside the source object and can crash the GC even if it is never dereferenced.
+ unsafe
public static ref T Subtract(ref T source, nuint elementOffset) where T : allows ref struct { throw null; }

+ /// Returns a mutable interior reference into a boxed value with no type check; a wrong T reinterprets the box's contents.
+ unsafe
public static ref T Unbox(object box) where T : struct { throw null; }

+ /// Writes a value of type T through a byte reference with no bounds or alignment check; may write past the end of the buffer.
+ unsafe
public static void WriteUnaligned(ref byte destination, T value) where T : allows ref struct { }
}
```

### API Usage

The annotated APIs will require `unsafe{}` context at their call site.

### 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.