Exception during ExecuteAsync throws Exception that cannot be handled

Abierto
#1,832 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
3/5
Tiempo estimado
1-2 días
Aptitud para principiantes
68/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
csharp
Área
networking

Línea de trabajo

Comienza en SshCommand.ExecuteAsync y sigue la secuencia de inicio desde la creación de _tcs, pasando por _channel.Open() y SendExecRequest(...); después, inspecciona cómo Dispose() gestiona una tarea incompleta. Verifica el comportamiento durante una excepción de inicio y asegúrate de que el fallo se devuelva o de que no quede ninguna tarea con errores oculta.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

SshCommand.ExecuteAsync appears to have a race during command startup.

In the current implementation, _tcs is created before _channel.Open() and _channel.SendExecRequest(...) are called:

_tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
_userToken = cancellationToken;

_channel.DataReceived += Channel_DataReceived;
_channel.ExtendedDataReceived += Channel_ExtendedDataReceived;
_channel.RequestReceived += Channel_RequestReceived;
_channel.Closed += Channel_Closed;
_channel.Open();

_ = _channel.SendExecRequest(CommandText);

return _tcs.Task;

If _channel.Open() or _channel.SendExecRequest(...) throws synchronously due to a connection/session teardown, the method exits before returning _tcs.Task.
If the caller uses the command in a using block, disposal may then execute while _tcs.Task is still incomplete:

if (_tcs is { Task.IsCompleted: false } tcs)
{
    _ = tcs.TrySetException(new ObjectDisposedException(GetType().FullName));
}

This can produce a faulted task that was never returned to the caller and therefore can never be awaited or observed. Later, that can surface via TaskScheduler.UnobservedTaskException.

Expected behavior

If command startup fails after _tcs has been created, the failure should not leave behind an orphaned faulted task.
Possible acceptable outcomes:

  • ExecuteAsync returns a faulted task, or
  • ExecuteAsync catches startup exceptions after _tcs creation, completes _tcs accordingly, and returns it, or
  • startup is arranged so no internal task exists before all synchronous failure points have passed.

Actual behavior

Under transient network/session failure, a caller can observe:

  • a synchronous exception from ExecuteAsync, and later
  • an unrelated TaskScheduler.UnobservedTaskException caused by the internal _tcs.Task being faulted during Dispose() without ever having been returned.

Observed trigger

In our case this happened around network loss / socket failure. We saw a preceding socket error similar to:

System.Net.Sockets.SocketException: A socket operation was attempted to an unreachable network

That seems consistent with the session being torn down while a command is being started.

Suggested fix direction

Wrap the startup section after _tcs creation in a try/catch, and ensure that any exception either:

  • completes _tcs and returns _tcs.Task, or
  • resets internal state so no hidden faulted task remains.

A minimal fix would likely be around the section between _tcs = new TaskCompletionSource(...) and return _tcs.Task.

Lenguaje dominante
C#
Estrellas
4.4k
Forks
993
Merge medio
9 d 21 h
PR fusionados (30 d)
1

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de sshnet/SSH.NET

Todos los issues de sshnet/SSH.NET

Issues similares

Más issues de C#

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.