ReleaseHandleFailed MDA

Note

Este artigo é específico para .NET Framework. Ele não se aplica a implementações mais recentes do .NET, incluindo o .NET 6 e versões posteriores.

O releaseHandleFailed MDA (assistente de depuração gerenciada) é ativado para notificar os desenvolvedores quando o ReleaseHandle método de uma classe derivada SafeHandle ou CriticalHandle retorna false.

Symptoms

Vazamentos de recursos ou memória. Se o ReleaseHandle método da classe derivar ou SafeHandleCriticalHandle falhar, o recurso encapsulado pela classe poderá não ter sido liberado ou limpo.

Cause

Os usuários deverão fornecer a implementação do ReleaseHandle método se criarem classes que derivam ou SafeHandleCriticalHandle; portanto, as circunstâncias são específicas para o recurso individual. No entanto, os requisitos são os seguintes:

  • SafeHandle e CriticalHandle os tipos representam wrappers em torno de recursos de processo vitais. Um vazamento de memória tornaria o processo inutilizável ao longo do tempo.

  • O ReleaseHandle método não deve falhar ao executar sua função. Depois que o processo adquire esse recurso, ReleaseHandle é a única maneira de liberá-lo. Portanto, a falha implica em vazamentos de recursos.

  • Qualquer falha que ocorra durante a execução de ReleaseHandle, impedindo a versão do recurso, é um bug na implementação do ReleaseHandle próprio método. É responsabilidade do programador garantir que o contrato seja cumprido, mesmo que esse código chame o código de autoria de outra pessoa para executar sua função.

Resolução

O código que usa o tipo específico SafeHandle (ou CriticalHandle) que gerou a notificação do MDA deve ser revisado, procurando locais em que o valor bruto do identificador seja extraído e SafeHandle copiado em outro lugar. Essa é a causa usual de falhas dentro SafeHandle ou CriticalHandle implementações, pois o uso do valor bruto do identificador não é mais acompanhado pelo runtime. Se a cópia do identificador bruto for fechada posteriormente, ela poderá fazer com que uma chamada posterior ReleaseHandle falhe porque o fechamento é tentado no mesmo identificador, o que agora é inválido.

Há várias maneiras pelas quais a duplicação de identificador incorreto pode ocorrer:

  • Procure chamadas para o DangerousGetHandle método. As chamadas para esse método devem ser extremamente raras e todas as que você encontrar devem estar cercadas por chamadas e DangerousAddRefDangerousRelease métodos. Esses últimos métodos especificam a região do código na qual o valor bruto do identificador pode ser usado com segurança. Fora dessa região ou se a contagem de referência nunca for incrementada em primeiro lugar, o valor do identificador poderá ser invalidado a qualquer momento por uma chamada para Dispose ou Close em outro thread. Depois que todos os usos tiverem DangerousGetHandle sido rastreados, você deverá seguir o caminho que o identificador bruto usa para garantir que ele não seja entregue a algum componente que eventualmente chamará CloseHandle ou outro método nativo de baixo nível que liberará o identificador.

  • Verifique se o código usado para inicializar com SafeHandle um valor de identificador bruto válido é o proprietário do identificador. Se você formar um SafeHandle identificador em torno de um identificador que seu código não possui sem definir o ownsHandle parâmetro false no construtor base, o proprietário do identificador real e do SafeHandle identificador real poderá tentar fechar o identificador, levando a um erro ReleaseHandle em caso de SafeHandle perda da corrida.

  • Quando um SafeHandle é empacotado entre domínios de aplicativo, confirme se a SafeHandle derivação que está sendo usada foi marcada como serializável. Nos casos raros em que uma classe derivada SafeHandle se tornou serializável, ela deve implementar a ISerializable interface ou usar uma das outras técnicas para controlar manualmente o processo de serialização e desserialização. Isso é necessário porque a ação de serialização padrão é criar um clone bit a bit do valor de identificador bruto delimitado, resultando em duas SafeHandle instâncias achando que possuem o mesmo identificador. Ambos tentarão chamar ReleaseHandle no mesmo identificador em algum momento. O segundo SafeHandle a fazer isso falhará. O curso de ação correto ao serializar um SafeHandle é chamar a DuplicateHandle função ou uma função semelhante para o tipo de identificador nativo para fazer uma cópia de identificador legal distinta. Se o tipo de identificador não der suporte a isso, o SafeHandle tipo de encapsulamento não poderá ser serializável.

  • Pode ser possível rastrear onde um identificador está sendo fechado mais cedo, levando a uma falha quando o ReleaseHandle método é finalmente chamado, colocando um ponto de interrupção do depurador na rotina nativa usada para liberar o identificador, por exemplo, a CloseHandle função. Isso pode não ser possível para cenários de estresse ou até mesmo testes funcionais de médio porte devido ao tráfego pesado com o qual essas rotinas geralmente lidam. Pode ajudar a instrumentar o código que chama o método de versão nativa, a fim de capturar a identidade do chamador ou, possivelmente, um rastreamento de pilha completo e o valor do identificador que está sendo liberado. O valor do identificador pode ser comparado com o valor relatado por este MDA.

  • Observe que alguns tipos de identificador nativos, como todos os identificadores do Win32 que podem ser lançados por meio da CloseHandle função, compartilham o mesmo namespace de identificador. Uma versão errônea de um tipo de identificador pode causar problemas com outra. Por exemplo, fechar acidentalmente um identificador de evento Win32 duas vezes pode levar a um identificador de arquivo aparentemente não relacionado sendo fechado prematuramente. Isso acontece quando o identificador é liberado e o valor do identificador fica disponível para uso para acompanhar outro recurso, potencialmente de outro tipo. Se isso acontecer e for seguido por uma segunda versão errônea, o identificador de um thread não relacionado poderá ser invalidado.

Efeito no Runtime

Esse MDA não tem nenhum efeito sobre o CLR.

Saída

Uma mensagem que indica que uma ou uma SafeHandleCriticalHandle falha ao liberar corretamente o identificador. Por exemplo:

"A SafeHandle or CriticalHandle of type 'MyBrokenSafeHandle'
failed to properly release the handle with value 0x0000BEEF. This
usually indicates that the handle was released incorrectly via
another means (such as extracting the handle using DangerousGetHandle
and closing it directly or building another SafeHandle around it."

Configuração

<mdaConfig>
  <assistants>
    <releaseHandleFailed/>
  </assistants>
</mdaConfig>

Example

Veja a seguir um exemplo de código que pode ativar o releaseHandleFailed MDA.

bool ReleaseHandle()
{
    // Calling the Win32 CloseHandle function to release the
    // native handle wrapped by this SafeHandle. This method returns
    // false on failure, but should only fail if the input is invalid
    // (which should not happen here). The method specifically must not
    // fail simply because of lack of resources or other transient
    // failures beyond the user’s control. That would make it unacceptable
    // to call CloseHandle as part of the implementation of this method.
    return CloseHandle(handle);
}

Consulte também