Vazamentos e erros de memória
Memory leak, use-after-free, double free, buffer overflow — quatro categorias de erros que custam bilhões por ano em exploits de segurança e crashes de produção. Entender cada um é o primeiro passo para evitá-los.
O que pode dar errado com o heap
Gerenciar memória manualmente é como alugar depósitos: você aluga (malloc), usa, e
devolve (free). Se esquecer de devolver, o depósito continua cobrado mas sem uso —
isso é um memory leak. Se devolver e depois tentar entrar de novo — isso é
use-after-free, e agora outra empresa pode estar lá dentro. Se tentar devolver
duas vezes — double free, o administrador do depósito fica confuso e o sistema
pode corromper. Se você sair dos seus limites e invadir o depósito do vizinho — isso é
buffer overflow.
Em produção, esses erros são frequentemente silenciosos — o programa funciona bem por horas ou dias antes de travar. Pior ainda, use-after-free e buffer overflow são exploits: um atacante pode controlar o que vai ser sobrescrito na memória e sequestrar a execução do programa.
Com vazamento vs. sem vazamento
Veja como o uso de memória evolui nos dois programas ao longo do tempo. O da esquerda aloca sem liberar — inevitavelmente esgota a RAM. O da direita libera após cada uso e permanece estável.
while True:
buf = malloc(1 KB)
processa(buf)
# esqueceu free(buf)!while True:
buf = malloc(1 KB)
processa(buf)
free(buf) # ✓Os quatro erros clássicos
// Os quatro erros clássicos de memória em C // 1. Memory leak — aloca mas nunca libera void vaza() { char* buf = malloc(1024); usa(buf); // esqueceu free(buf)! → 1 KB perdido para sempre } // buf sai de escopo, mas o bloco no heap permanece // 2. Use-after-free (dangling pointer) — usa após liberar int* p = malloc(sizeof(int)); *p = 42; free(p); printf("%d\n", *p); // UNDEFINED BEHAVIOR — o bloco pode ser // reutilizado por outra alocação! // 3. Double free — libera duas vezes free(p); // segunda vez → corrompe o heap → crash // 4. Buffer overflow — escreve além dos limites char buf[4]; buf[4] = 'X'; // índice 4 está fora! sobrescreve memória adjacente
Como detectar e prevenir
// Ferramentas para detectar erros de memória // Valgrind — detecta leaks e use-after-free em runtime // $ valgrind --leak-check=full --error-exitcode=1 ./programa // ==1234== LEAK SUMMARY: // ==1234== definitely lost: 1,024 bytes in 1 block // ==1234== at 0x4C2FB0F: malloc (vg_replace_malloc.c) // ==1234== by 0x400537: vaza (main.c:4) // AddressSanitizer (ASan) — mais rápido, integrado ao compilador // $ gcc -fsanitize=address -g programa.c && ./a.out // ERROR: AddressSanitizer: heap-use-after-free on address 0x... // Linguagens com segurança automática: // Java / Python / Go / JS → GC elimina leaks e dangling ptrs // Rust → borrow checker (tempo de compilação) // C++ moderno → unique_ptr / shared_ptr / RAII
unique_ptr e
shared_ptr usam RAII para gerenciar heap — sem free manual, sem leak, sem double
free. É o meio-termo entre C (manual) e GC (automático).
Caçando bugs de memória
Mini projeto: escreva deliberadamente os quatro erros em um programa C (leak, use-after-free, double free, overflow) e rode cada um com Valgrind ou ASan. Documente a mensagem de erro gerada por cada um. Qual é mais difícil de detectar? Por quê?
Projeto principal: em C++, implemente um SmartPtr<T> (unique pointer simplificado) com constructor, destructor e operator*. O destructor deve chamar delete automaticamente. Adicione um contador de instâncias ativas para verificar que nenhuma instância fica aberta após o escopo terminar. Teste com um tipo que imprime no construtor e no destructor.
Desafio extra: escreva um fuzzer simples: um loop que chama sua função alvo com strings/bytes aleatórios de tamanhos crescentes, rodando com ASan ativo. Se ela crashar, o ASan imprime o stack trace exato. Implemente um parser de URL ingênuo e tente encontrar um buffer overflow nele com seu fuzzer.
Teste sua intuição
free(p); printf("%d", *p); — que tipo de erro é esse?
char buf[4]; buf[10] = 'X'; — que problema isso causa?
Onde você encontra isso
Segurança — exploits de buffer overflow
Um buffer overflow na stack pode sobrescrever o endereço de retorno de uma função. Com controle desse endereço, um atacante redireciona a execução para código malicioso injetado. Técnicas como stack canaries, NX bit e ASLR foram criadas especificamente para dificultar esse ataque — que existe desde os anos 80.
Navegadores e sandboxing
Chrome, Firefox e Safari executam abas em processos separados exatamente porque um use-after-free em código JavaScript pode vazar para o motor de renderização. Se o ataque se limitar a um processo isolado sem permissões, o dano é contido. Dezenas de CVEs de browser por ano são use-after-free em C++.
A aposta do Rust
A Casa Branca dos EUA publicou em 2024 um guia recomendando que o setor de tecnologia migre de C/C++ para linguagens memory-safe como Rust. O NSA, Google, Microsoft e a Linux Foundation estimam que 70% das vulnerabilidades de segurança críticas são erros de memória — o problema que o borrow checker do Rust resolve em tempo de compilação.