Segurança e condições de corrida

Outra área de preocupação é o potencial de falhas de segurança exploradas pelas condições de corrida. Isso pode acontecer de várias maneiras. Os subtópicos a seguir descrevem algumas das principais armadilhas que o desenvolvedor deve evitar.

Condições de corrida no método Dispose

Se o método Dispose de uma classe (para obter mais informações, confira Coleta de Lixo) não for sincronizado, o código de limpeza dentro de Dispose poderá ser executado mais de uma vez, conforme mostra o exemplo a seguir.

Sub Dispose()  
    If Not (myObj Is Nothing) Then  
       Cleanup(myObj)  
       myObj = Nothing  
    End If  
End Sub  
void Dispose()
{  
    if (myObj != null)
    {  
        Cleanup(myObj);  
        myObj = null;  
    }  
}  

Como essa implementação de Dispose não é sincronizada, Cleanup poderá ser chamado primeiro por um thread e então por um segundo thread antes que _myObj seja definido como nulo. Dependendo do que acontece quando o código Cleanup é executado, isso poderá ser uma preocupação de segurança. Um grande problema com implementações de Dispose não sincronizadas envolve o uso de identificadores de recursos, como arquivos. O descarte inadequado pode levar ao uso de um identificador incorreto, o que costuma causar vulnerabilidades de segurança.

Condições de corrida em construtores

Em alguns aplicativos, outros threads podem conseguir acessem membros da classe antes que seus construtores de classe sejam completamente executados. Você deve examinar todos os construtores de classe para garantir que não haja problemas de segurança se isso acontecer, ou sincronizar threads, se necessário.

Condições de corrida com objetos armazenados em cache

Código que armazena em cache informações de segurança ou usa a operação assert de segurança de acesso de código também pode estar vulnerável a condições de corrida se outras partes da classe não forem sincronizadas adequadamente, conforme mostra o exemplo a seguir.

Sub SomeSecureFunction()  
    If SomeDemandPasses() Then  
        fCallersOk = True  
        DoOtherWork()  
        fCallersOk = False  
    End If  
End Sub  
  
Sub DoOtherWork()  
    If fCallersOK Then  
        DoSomethingTrusted()  
    Else  
        DemandSomething()  
        DoSomethingTrusted()  
    End If  
End Sub  
void SomeSecureFunction()
{  
    if (SomeDemandPasses())
    {  
        fCallersOk = true;  
        DoOtherWork();  
        fCallersOk = false;  
    }  
}  
void DoOtherWork()
{  
    if (fCallersOK)
    {  
        DoSomethingTrusted();  
    }  
    else
    {  
        DemandSomething();  
        DoSomethingTrusted();  
    }  
}  

Se houver outros caminhos para DoOtherWork que possam ser chamados de outro thread com o mesmo objeto, um chamador não confiável poderá passar por uma demanda.

Se o código armazenar em cache informações de segurança, verifique se você as examinou para essa vulnerabilidade.

Condições de corrida em finalizadores

As condições de corrida também podem ocorrer em um objeto que faz referência a um recurso estático ou não gerenciado que ele libera em seu finalizador. Se vários objetos compartilharem um recurso manipulado no finalizador de uma classe, os objetos deverão sincronizar todo o acesso a esse recurso.

Confira também