Os estouros de buffer são grandes. Nada em C é verificado no intervalo por padrão, por isso é muito fácil substituir um buffer. Há uma função de biblioteca padrão gets(), que não pode ser impedida de estourar o buffer e quase nunca deve ser usada.
Existem algumas técnicas no nível de implementação para impedir a exploração, como embaralhar blocos de heap, mas isso não interrompe o estouro de buffer nos buffers locais, o que geralmente pode fazer coisas interessantes, como alterar o endereço para o qual a função retornará.
Não existe uma boa solução geral em C. Muitas funções da biblioteca possuem versões que limitarão a quantidade que serão gravadas. embora calcular isso possa ser desajeitado. Existem softwares que podem detectar estouros de buffer de heap em teste, desde que o teste apropriado seja executado, e o estouro de pilha geralmente aparece como uma falha nos testes. Fora isso, é uma questão de codificação cuidadosa e revisão de código.
Um problema relacionado é o problema de gravar em um buffer muito pequeno por um caractere, esquecendo que uma string C com n caracteres requer n + 1 caracteres na memória, devido ao '\0'terminador. Se o invasor conseguir armazenar uma string sem o terminador, qualquer função C que espera uma string continuará processando até atingir um byte zero, o que pode resultar em copiar ou emitir mais informações do que o desejado (ou atingir a memória protegida para um ataque do DOS) ) A solução, novamente, é a conscientização, o cuidado e as revisões de código.
Há outro risco com a printf()família. Se você escreve char * str; ... printf(str);, está se preparando para problemas se strcontiver um '%' quando impresso. A %ndiretiva de formato permite printf()gravar na memória. A solução é printf("%s", str);ou puts(str);. (Além disso, use o C99 em snprintf()vez de sprintf().)
O uso de números inteiros não assinados, principalmente como índices de loop, pode causar problemas. Se você atribuir um pequeno valor negativo a um não assinado, obterá um grande valor positivo. Isso pode prejudicar coisas como processar apenas N instâncias de algo ou funções limitadas como strncpy(). Examine todos os números inteiros não assinados. Você pode evitar unsigned short, pois um grande valor em um deles será convertido em um grande valor positivo em um int.
Não esqueça que uma constante de caractere, em C, é na verdade uma int. Escrever algo como char c; while((c = getchar()) != EOF) ...pode falhar facilmente, pois EOFnão será representável em um arquivo char.
Há muitos erros característicos de C em que posso pensar, mas esses podem causar problemas de segurança.