A computação distribuída consiste em tempo e estado. Isto é, para que mais de um componente se comunique, é necessário compartilhar o estado, o que exige tempo.
A maioria dos programadores antropomorfiza seu trabalho. Eles enxergam um thread de controle executando todo o programa da mesma forma como enxergariam a si mesmos fazendo o trabalho inteiro por conta própria. Computadores modernos, entretanto, alternam entre tarefas com muita rapidez e, em sistemas multi-core, multi-CPU ou distribuídos, dois eventos podem ocorrer exatamente ao mesmo tempo. Defeitos rapidamente são postos nas lacunas entre o modelo do programador de como um programa é executado e o que ocorre na realidade. Esses defeitos estão relacionados com interações inesperadas entre threads, processos, tempo e informações. Essas interações ocorrem por meio de estados compartilhados: semáforos, variáveis, o sistema de arquivos e, basicamente, todas as coisas capazes de armazenar informações.
Race Condition: Signal Handling
Condições de corrida de manipulação de sinais apresentam mais chances de ocorrer quando:
1. O programa instala um único manipulador de sinais para mais de um sinal.
2. Dois sinais diferentes para os quais o manipulador está instalado chegam em curta sucessão, causando uma condição de corrida no manipulador de sinais.
Exemplo: O código a seguir instala o mesmo manipulador de sinais simples não reentrante para dois sinais diferentes. Se um invasor fizer com que os sinais sejam enviados nos momentos certos, o manipulador de sinais passará por uma vulnerabilidade de liberação dupla. Chamar
free()
duas vezes no mesmo valor pode resultar em um buffer overflow. Quando um programa chama free()
duas vezes com o mesmo argumento, as estruturas de dados de gerenciamento de memória desse programa se tornam corrompidas. Essa corrupção pode fazer com que o programa trave ou, em algumas circunstâncias, com que duas chamadas posteriores para malloc()
retornem o mesmo apontador. Se malloc()
retornar o mesmo valor duas vezes, e, mais tarde, o programa der ao invasor controle sobre os dados que são gravados nessa memória duplamente alocada, o programa se tornará vulnerável a um ataque de buffer overflow.
void sh(int dummy) {
...
free(global2);
free(global1);
...
}
int main(int argc,char* argv[]) {
...
signal(SIGHUP,sh);
signal(SIGTERM,sh);
...
}