Regra
Prevenir comum segmentação falhas padrões.
Nulo ponteiro desreferências, buffer buffer,
e uso depois de livre erros causam crashes e segurança vulnerabilidades.
Linguagens suportadas: C/C++Introdução
As falhas de segmentação continuam a ser a fonte mais comum de falhas e vulnerabilidades exploráveis em aplicações C/C++. Estas violações de acesso à memória ocorrem quando o código tenta ler ou escrever memória que não lhe pertence, normalmente através de dereferências de ponteiro nulo, estouros de buffer, ponteiros pendentes ou acesso a memória libertada. Um único segfault pode derrubar servidores de produção, mas pior, muitos padrões de segfault são exploráveis para execução arbitrária de código.
Porque é importante
Implicações de segurança: Os estouros de buffer e as vulnerabilidades use-after-free são a base da maioria das explorações de corrupção de memória. Os atacantes aproveitam-nas para sobrescrever endereços de retorno, injetar shellcode ou manipular o fluxo de controlo do programa. A vulnerabilidade Heartbleed de 2014 foi uma leitura excessiva de um buffer. As explorações modernas continuam a visar estes padrões porque permitem o acesso direto à memória por parte dos atacantes.
Estabilidade do sistema: As falhas de segmentação fazem com que a sua aplicação falhe imediatamente, sem degradação graciosa. Em sistemas de produção, isto significa pedidos perdidos, transacções interrompidas e estado corrompido. Ao contrário das exceções de linguagens de alto nível que podem ser capturadas, as falhas de segmentação encerram o processo, exigindo procedimentos de reinicialização e recuperação.Expansão da superfície de ataque: Cada desreferência de ponteiro não verificada, strcpy, memcpyO acesso a um array sem verificação de limites é um potencial ponto de entrada para exploração. Os atacantes encadeiam estas vulnerabilidades, utilizando uma para corromper a memória de forma a permitir a exploração de outra.
Exemplos de código
Não conforme:
void process_user_data(const char* input) {
char buffer[64];
strcpy(buffer, input); // No bounds checking
char* token = strtok(buffer, ",");
while (token != NULL) {
process_token(token);
token = strtok(NULL, ",");
}
}
int* get_config_value(int key) {
int* value = (int*)malloc(sizeof(int));
*value = lookup_config(key);
return value; // Caller must free, but no documentation
}Porque é que não é seguro: O strcpy() causa um estouro de buffer se a entrada exceder 63 bytes, permitindo que os atacantes sobrescrevam a memória da pilha. A chamada get_config_value() vaza memória em cada chamada e cria o risco de ponteiro pendente se quem chama libera a memória enquanto outro código ainda faz referência a ela.
Conformidade:
void process_user_data(const char* input) {
if (!input) return;
size_t input_len = strlen(input);
char* buffer = malloc(input_len + 1);
if (!buffer) return;
strncpy(buffer, input, input_len);
buffer[input_len] = '\0';
char* token = strtok(buffer, ",");
while (token != NULL) {
process_token(token);
token = strtok(NULL, ",");
}
free(buffer);
}
int get_config_value(int key, int* out_value) {
if (!out_value) return -1;
*out_value = lookup_config(key);
return 0; // Caller owns out_value memory
}Porque é que é seguro: As verificações de ponteiro nulo evitam falhas de desreferência. A alocação dinâmica elimina os limites fixos de tamanho de buffer. A cópia com limites verificados com strncpy() evita o transbordamento. Semântica de propriedade clara em get_config_value() em que o chamador fornece a memória, para evitar confusões e fugas de informação.
Conclusão
A segurança da memória em C e C++ requer uma programação defensiva em cada operação de ponteiro e atribuição de memória. As falhas de segmentação não são inevitáveis, são evitáveis através de verificações nulas consistentes, validação de limites e padrões claros de propriedade de memória. A deteção desses padrões antes da produção evita tanto falhas quanto vulnerabilidades exploráveis.
.avif)
