Temporary Scalars can be finalized while native code still uses them (crash in Tensor.add under concurrency)
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 45/100
Línea de trabajo
Start with the implicit Scalar conversions and native call in src/TorchSharp/Tensor/Tensor.Math.cs, then review the referenced issues #1434 and #1496 for existing approaches. Check other examples listed in the report, including softplus and scalar operators, to understand the scope. Done means every Scalar passed to native code stays alive until the call returns; the issue does not specify a deterministic reproduction test.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Describe the bug
A temporary Scalar that TorchSharp creates for a native call can be freed by its finalizer while libtorch is still reading it. That is a use-after-free: it can crash the process or give wrong results.
Scalar frees its native value in a finalizer, isn't a SafeHandle, and isn't tracked by any DisposeScope. Many methods create one implicitly and pass only its handle to native code, e.g. src/TorchSharp/Tensor/Tensor.Math.cs:
public Tensor add(Tensor target)
{
return add(target, 1); // temporary `Scalar` for alpha
}
public Tensor add(Tensor target, `Scalar` alpha)
{
var res = THSTensor_add(Handle, target.Handle, alpha.Handle); // nothing keeps alpha alive from here on
...
}
Once .Handle has been read, the JIT may consider the Scalar unreachable. A garbage collection that starts during the native call (for example one triggered by another thread's allocations) can then run ~Scalar() → THSTorch_dispose_scalar mid-call.
The same pattern is everywhere a .NET number is implicitly converted to a Scalar argument:
- add/sub with their default alpha, and tensor +;
- scalar arithmetic operators;
- masked_fill(mask, 0), pow(2), eq(0);
- functions like softplus, which convert their double parameters internally.
#1434 and #1496 already dispose these temporaries, framed as a memory leak. They would also fix this crash, because a Scalar that is disposed after the call stays alive during it. This issue is to report that the problem is a memory-safety bug as well as a leak, which may help prioritize those PRs.
To Reproduce
The failure is intermittent and depends on GC timing, so there's no deterministic repro. We hit it in StanzaSharp, which runs Stanza's NLP models on TorchSharp. In a CI test, 8 threads run inference concurrently on shared modules, each thread inside its own DisposeScope and no_grad(). The failing line was:
var predicted = 1 + torch.nn.functional.softplus(dist);
Crash (process terminated):
System.Runtime.InteropServices.ExternalException (0x80004005): Expected false to be true, but got false.
Exception raised from toLong at C:\a\pytorch\pytorch\pytorch\c10/core/Scalar.h:140 (most recent call first):
c10.dll!c10::detail::torchCheckFail
torch_cpu.DLL!at::native::add
torch_cpu.DLL!at::Tensor::add
at TorchSharp.torch.CheckForErrors()
at TorchSharp.torch.Tensor.op_Addition(Int32 left, Tensor right)
...
It crashed once on Windows Arm64 and passed on a rerun. It never reproduced on x64, even with a thread looping GC.Collect(); GC.WaitForPendingFinalizers(); during inference. Presumably the x64 JIT happens to keep the temporary alive across the call, but nothing guarantees it.
Expected behavior
Every Scalar passed to native code stays alive until the call returns, whatever the GC does.
Please complete the following information:
- OS: Windows 11 on Arm64 (GitHub Actions windows-11-arm runner), .NET 10
- Package Type: TorchSharp 0.107.0 with libtorch-cpu-win-arm64 2.10.0
- Version: 0.107.0
Additional context
Possible fixes, any of:
- merge #1434 / #1496 (dispose temporaries right after the call);
- add
GC.KeepAlive(scalar)after each P/Invoke that takes aScalarhandle; - make
Scalarwrap a SafeHandle, which keeps it alive for the duration of every P/Invoke and fixes all call sites at once.
Tensor temporaries created outside a DisposeScope probably have the same exposure.
Our workaround: never let TorchSharp create a Scalar. We keep our own in static readonly fields or using var locals, and call the overloads that take them, e.g. a.add(b, one) instead of a + b
- Lenguaje dominante
- C#
- Estrellas
- 1.9k
- Forks
- 230
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin 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/TorchSharp
-
Dificultad 4/5 Más de una semana Aptitud para principiantes 30/100
dotnet/TorchSharp#1581 ·
-
Dificultad 5/5 Más de una semana Aptitud para principiantes 25/100
dotnet/TorchSharp#1579 ·
-
bug
Dificultad 4/5 3-5 días Aptitud para principiantes 56/100
dotnet/TorchSharp#1574 ·
-
bug
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
dotnet/TorchSharp#1567 ·
-
question
Dificultad 5/5 Más de una semana Aptitud para principiantes 25/100
dotnet/TorchSharp#1557 ·
Todos los issues de dotnet/TorchSharp
Issues similares
-
CI: миграции на пустой PostgresAbiertotrack:release
Dificultad 2/5 1-3 horas Aptitud para principiantes 80/100
-
[Bug] [Go] Ack returns nil when the server answers with a non-OK statusPosiblemente ocupada @leizhiyuan la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
apache/rocketmq-clients#1465 ·
Los mantenedores suelen responder en 2 días
-
fix(testing): HeadlessTerminal disposes services synchronouslyPosiblemente ocupada @zidad la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 69/100
RazorConsole/RazorConsole#388 ·
Los mantenedores suelen responder en 1 día
-
A practical example of rulerAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
starlinghq/starling#209 ·
Los mantenedores suelen responder en 1 día