Skip to content

UnsafeAccessorType will bypass Native Profiler assembly redirection for DiagnosticSource (v11+) on .NET 10+ #4923

@alexeypukhov

Description

@alexeypukhov

Bug Report

UnsafeAccessorType reflection will bypass Native Profiler assembly redirection for DiagnosticSource (v11+) on .NET 10+ (post PR #4783)

Summary

The UnsafeAccessorTypeAttribute (introduced in .NET 10) bypasses our native profiler's assembly redirection mechanism, which will cause type and state drift on .NET 10+ when OpenTelemetry upgrades to System.Diagnostics.DiagnosticSource 11.

This issue is not currently reproducible but will manifest once we switch to DS 11 after .NET 11 is released.

Background: Native Profiler Assembly Redirection

Our assembly conflict resolution approach (introduced in PR #4783) works as follows:

  • Native profiler (cor_profiler.cpp) redirects AssemblyRefs for key dependencies using the redirection map in assembly_redirection_net.h
  • Managed AssemblyResolver subscribes to DefaultALC.Resolving event to handle redirected dependencies
  • When a higher version than the app originally referenced is needed, we load it into a custom ALC to avoid runtime crashes

Goal: Prevent version conflicts by ensuring all code paths reference our redirected (higher) dependency versions.

The Problem: UnsafeAccessor Bypasses Redirection

.NET 10 introduced UnsafeAccessorTypeAttribute, a JIT-level reflection mechanism:

  • Native redirection of AssemblyRef does not affect assembly resolution during reflection
  • Unlike traditional managed reflection, it performs JIT-level type resolution that sticks to the ALC context of the requesting assembly, bypassing CurrentContextualReflectionContext (so customALC.EnterContextualReflection() has no effect)

Why UnsafeAccessorType breaks Native Assembly redirection: When the runtime is requested to load an assembly from Default ALC and can find it (e.g. in TPA list), it loads immediately to Default ALC—there is no opportunity to interfere.

General impact: Any assembly in Default ALC using UnsafeAccessorType to reference our redirected dependencies will cause drift when our version is higher than what originally referenced.

Why This Is Especially Critical for OpenTelemetry

The .NET runtime itself (System.Private.CoreLib) uses UnsafeAccessorType during initialization to access types from DiagnosticSource:

[UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "GetInstance")]
[return: UnsafeAccessorType("System.Diagnostics.Metrics.MetricsEventSource, System.Diagnostics.DiagnosticSource")]
static extern object GetInstance(
    [UnsafeAccessorType("System.Diagnostics.Metrics.MetricsEventSource, System.Diagnostics.DiagnosticSource")] object? _);

Source: EventSourceInitHelper in System.Private.CoreLib

Why this is critical: OpenTelemetry initialization triggers this runtime code path. DiagnosticSource is foundational to OpenTelemetry and must be loaded exactly once.

Current State and Future Break Scenario

Why not reproducible now:
For .NET 10, we have no DiagnosticSource redirection in assembly_redirection_net.h. This is intentional: DS 10 ships with .NET 10 and satisfies OpenTelemetry's requirements, loading to Default ALC without conflict.

What breaks when .NET 11 releases:
When .NET 11 ships with DS 11, OpenTelemetry will upgrade accordingly. All .NET versions—including .NET 10—will receive a DS 11 redirection.

For .NET 10 applications:

  1. Runtime (System.Private.CoreLib) loaded in Default ALC uses UnsafeAccessorType to load MetricsEventSource from DS
  2. Our AssemblyRef redirection does not apply, and JIT reflection ignores contextual reflection—DS 10 is resolved from TPA and loaded immediately to Default ALC without raising the Resolving event
  3. Our AssemblyResolver (via other code paths) loads DS 11 to custom ALC
  4. Result: Two DS versions across ALCs → type drift and state drift

Proposed Solution

Extend the native profiler to update UnsafeAccessorTypeAttribute assembly references (not just AssemblyRefs) when bumping dependency versions. This ensures UnsafeAccessor resolution respects redirections and triggers the Resolving event handler.

Status: I have a POC that successfully redirects UnsafeAccessorType definition which results in triggering the Resolving event.

Related Issue

This issue is related to: Issue #4924 - UnsafeAccessorType breaks StartupHook isolation. Both stem from UnsafeAccessorType bypassing our assembly conflict resolution mechanisms, but affect differently different deployment modes.

Runtime environment

  • OpenTelemetry Automatic Instrumentation version: Discovered during work on PR #4783
  • OS: All platforms
  • .NET version: .NET 10+ (will manifest when .NET 11 is released)
  • Deployment mode: Native Profiler deployment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Backlog

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions