`ref`/`out ComPtr<T>` overloads don't pin: a GC during the native call loses the result and corrupts the heap
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 30/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- csharp
Línea de trabajo
The fix is in the code generator: src/Core/Silk.NET.BuildTools/Overloading/Complex/ComPtrOverloader.cs, where the generated ref and out ComPtr<T> overloads pass param.GetAddressOf() at lines 61 and 95. ComPtr<T>.GetAddressOf() in Silk.NET.Core/Native/ComPtr1.csreturns an unpinned address. Done means the generated overloads pin the parameter withfixedand regenerated bindings no longer callGetAddressOf(), with a GC-stress repro (a Holderclass with aComPtr` field, compacting GCs on another thread) returning a non-null device.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Summary
The generated overloads that take a ref ComPtr<T> or out ComPtr<T> pass param.GetAddressOf() to the native function (ComPtrOverloader.cs, lines 61 and 95).
ComPtr<T>.GetAddressOf() is (T**) Unsafe.AsPointer(ref Unsafe.AsRef(in this)), so the pointer is not pinned.
When the ComPtr<T> is a field of a class (or an element of an array), another thread can run a GC during the native call and move the object. The native function then writes its result to the old address:
- The call returns
S_OK, but the field is stillnull. - One pointer per output is written into GC heap memory that may now belong to another object. In our runs this crashed the process inside the GC ("Internal CLR error 0x80131506").
Input arrays (e.g. OMSetRenderTargets(n, ref views[0], dsv) with a ComPtr<T>[] field) have the same problem: the native function can read stale memory.
Steps to reproduce
- Platform: Desktop (Windows 11, WARP)
- Framework Version: .NET 10
- API: Direct3D11 (same code path in Direct3D12, DXGI, D3DCompiler)
- Silk.NET 2.23.0
class Holder
{
public ComPtr<ID3D11Device> Device;
public ComPtr<ID3D11DeviceContext> Context;
}
// Thread 2: allocate and call GC.Collect(2, GCCollectionMode.Forced, blocking: true, compacting: true) in a loop.
// Thread 1:
var holder = new Holder();
int hr = d3d11.CreateDevice(default(ComPtr<IDXGIAdapter>), D3DDriverType.Warp, default, 0, null, 0,
D3D11.SdkVersion, ref holder.Device, null, ref holder.Context);
// hr == S_OK, holder.Device.Handle == null
- Run thread 2 (compacting GCs) in the background.
- Call
CreateDeviceas above in a loop, with a newHoldereach time.
Every call where the holder moved during CreateDevice returned S_OK with a null device and a null context, and some runs crashed the process. The same code with locals, or with fixed (ID3D11Device** p = &holder.Device.Handle) and the raw overload, never failed.
In a real project (Stride) this showed up on CI as a NullReferenceException in one random D3D11 test, in some runs.
Silk's own D3D11 tutorial has the same pattern (Tutorial 1.2 - Hello quad/Program.cs): its ComPtr locals are captured by window.Load += OnLoad, so the compiler stores them in a closure object on the heap, and they are passed as ref device, ref swapchain, ref vertexBuffer, ...
Comments
Scope in 2.23.0:
| Package | GetAddressOf() calls in generated overloads |
|---|---|
| Silk.NET.Direct3D11 | 4,182 |
| Silk.NET.Direct3D12 | 5,210 |
| Silk.NET.DXGI | 1,123 |
| Silk.NET.Direct3D.Compilers | 10,127 |
The other overload kinds are fine: ref T, ref T* and ref readonly T use fixed, spans forward to them (and there is no Span<ComPtr<T>> overload), this ComPtr<T> receivers are by value, and SilkMarshal.GuidPtrOf<T>() points to type-associated native memory.
Suggested fix: in the generated overloads, pin the parameter instead of calling GetAddressOf():
fixed (ComPtr<TI1>* ppDevicePtr = &ppDevice)
{
return CreateDevice(..., (ID3D11Device**) ppDevicePtr, ...);
}
fixed on a ref pins the containing object when it is on the heap, and costs almost nothing when it is on the stack.
GetPinnableReference() (ref *GetAddressOf()) has the same problem: fixed (T** p = someComPtrField) pins nothing.
Until then, a note in the docs would help: never pass a ComPtr<T> that lives on the heap to a ref/out ComPtr<T> overload.
- Lenguaje dominante
- C#
- Estrellas
- 5.2k
- Forks
- 482
- Merge medio
- 1 d 20 h
- PR fusionados (30 d)
- 5
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de dotnet/Silk.NET
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 Medio día Aptitud para principiantes 40/100
Los mantenedores suelen responder en 1 día
-
bug
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
Los mantenedores suelen responder en 1 día
-
bug good first issue verify-3.0
Dificultad 4/5 3-5 días Aptitud para principiantes 52/100
dotnet/Silk.NET#2610 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
Todos los issues de dotnet/Silk.NET
Issues similares
-
clawsweeper:fix-shape-clear clawsweeper:needs-maintainer-review clawsweeper:no-new-fix-pr clawsweeper:not-repro-on-main impact:ux-release-blocker issue-rating: 🦪 silver shellfish P0
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
openclaw/openclaw-windows-node#1843 · 1 comentario · 1 reacción ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
-
kind:ux size:S type:bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
elsa-workflows/elsa-foundation#3256 ·
Los mantenedores suelen responder en 1 día
-
[Bug]: Revit selector reads process ids 2000-2099 as years (flaky AgentClientTests.AYearOrAProcessIdSelectsTheHost)Posiblemente ocupada @Wurschdhaud la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 83/100
pyrevitlabs/pyRevit#3817 ·
Los mantenedores suelen responder en 1 día
-
bug effort:S P2
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
nightscout/nocturne#2449 ·
Los mantenedores suelen responder en 1 día