Annotate libraries with caller-unsafe keyword
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
Per the description in https://github.com/dotnet/designs/blob/main/accepted/2025/memory-safety/caller-unsafe.md, we need to annotate functions that should be considered unsafe.
Links to API Proposals:
- https://github.com/dotnet/runtime/issues/126956
- https://github.com/dotnet/runtime/issues/127098
- https://github.com/dotnet/runtime/issues/129750
- https://github.com/dotnet/runtime/issues/128075
- https://github.com/dotnet/runtime/issues/129751
### Criteria
The [doc](https://github.com/dotnet/designs/blob/main/accepted/2025/memory-safety/caller-unsafe.md) defines a safe API when it meets two criteria:
- Code never accesses memory that is not managed by the runtime
- No access to uninitialized memory
Here are a few more fine-grained criteria based on those two:
- **May create a potentially incorrect managed pointer**. Examples include all `Unsafe.Add*` and `Unsafe.Subtract*`. Such pointers may crash an app even when not directly dereferenced anywhere.
- **May create a managed pointer that should never be directly dereferenced**. Example: `MemoryMarshal.GetArrayDataReference` over an empty array.
- **Dereferences a caller-supplied unmanaged address** - reads or writes through a raw `IntPtr`/`nint`/`T*` parameter (or any kind of wrapper with those e.g. `SafeHandle`) directly or just lets it flow into a system where it might be dereferenced later.
- **May strip readonly-ness from a pointer**. Example: `Unsafe.AsRef` removing readonly and making a pointer to a readonly memory (e.g. RVA) writeable. This also may violate security invariants a type may hold.
- **May read uninitialized data**. Examples include reading padding/alignment in structs via various `MemoryMarshal` APIs, `Unsafe.BitCast`, `Unsafe.As`. Also, various `Unsafe.SkipInit`, `GC.AllocateUninitialized*`, etc.
- **May set an unsafe field without an `unsafe` context.** A good example is `MemoryMarshal.Read(ROS)` where T might theoretically have an unsafe field that this API lets you set without an `unsafe` context.
- **Produces a misaligned pointer** from a properly aligned input. Examples: `MemoryMarshal.Cast`, `[StructLayout(LayoutKind.Explicit)]`.
### Edge-cases
A section for edge-cases if any.
- Sometimes it's just not realistic to mark some widely used interface as unsafe e.g. `IDisposable.Dispose` to make some particular dangerous Dispose unsafe to call. In that case we need annotate all APIs that create that kind object as unsafe. Example: `GCHandle.Dispose` may lead to a global state corruption if called twice (e.g. on two copies of the same handle).
- All `extern`/`[LibraryImport]`/`InternalCall` APIs are considered unsafe by default unless explicitly annotated with `safe` keyword.
### Out of scope
- Reflection. Eventually we will likely make it mostly unsafe, but today it's completely out of scope.
- Shapes considered safe today because C# doesn't have lifetimes/ownership, allows static mutable state, etc.
- Thread-safety (although there are some intersections, it's generally considered orthogonal to memory safety)
- APIs in OOB packages (for now)
Contributor guide
Assessment
This issue has not been assessed yet.