- Foi descoberto um vazamento grave no emulador de terminal Ghostty que, quando executado por longos períodos, podia consumir dezenas de GB de memória
- A causa do problema era a lógica não padronizada de reutilização de páginas de memória na struct
PageList, na qual munmap não era chamado, fazendo com que memória não liberada se acumulasse
- Como o Claude Code CLI gera com frequência saídas de grafos com múltiplos code points, o uso de páginas não padronizadas aumentou, expondo o vazamento em grande escala
- A correção foi alterada para não reutilizar páginas não padronizadas e liberá-las imediatamente, e o rastreamento e a validação do vazamento foram feitos com o uso do recurso de tags de VM do macOS
- Essa correção é considerada a solução para o maior problema de vazamento do Ghostty e deve ser incluída em uma futura versão (1.3)
Visão geral do vazamento de memória do Ghostty
- Alguns usuários relataram casos em que o Ghostty consumia mais de 37 GB de memória após ficar em execução por muito tempo
- O vazamento existia pelo menos desde a versão 1.0, e aplicativos CLI recentes passaram a satisfazer certas condições que expuseram o problema
- A correção já foi mesclada no GitHub e deve ser incluída nas builds nightly e na versão estável 1.3
Estrutura do PageList e forma de gerenciamento de memória
- O Ghostty usa uma estrutura de lista duplamente ligada chamada PageList para armazenar o conteúdo do terminal
- Cada página inclui dados como caracteres, estilos e hyperlinks
- As páginas são alocadas com
mmap e reutilizadas por meio de um pool de páginas de tamanho padrão
- Páginas com tamanho igual ou inferior ao padrão retornam ao pool
- Páginas de tamanho não padrão devem ser liberadas diretamente com
munmap
- A estrutura em si funciona corretamente, mas um bug na lógica de otimização causava o vazamento
Otimização do scrollback e causa do bug
- Quando o Ghostty ultrapassa
scrollback-limit, ele faz uma otimização que reutiliza a página mais antiga
- O desempenho melhora porque apenas os ponteiros são ajustados, sem alocar uma nova página
- O problema é que, nesse processo, apenas os metadados da página não padronizada eram alterados para o tamanho padrão, enquanto a memória real permanecia a mesma
- Depois, ao liberar, ela era confundida com uma página padrão e
munmap não era chamado
- Como resultado, páginas não padronizadas não eram liberadas e se acumulavam, causando um grande vazamento de memória em execuções longas
Claude Code e a exposição do vazamento em grande escala
- O Claude Code CLI gera com frequência saídas de grafos com múltiplos code points, o que aumenta a frequência de uso de páginas não padronizadas
- Além disso, como há muita saída no scrollback, o vazamento se acumula rapidamente
- Pelo design do Ghostty, páginas não padronizadas deveriam ocorrer raramente, mas as características do Claude Code fizeram com que o vazamento fosse reproduzido em grande volume
- O desenvolvedor deixou claro que esse bug não é um problema do Claude Code, mas uma falha na lógica interna do Ghostty
Conteúdo da correção
Rastreando o vazamento com tags de VM
- Foi usado o recurso de tags de VM do kernel Mach no macOS para atribuir tags específicas às alocações de memória do PageList
- Durante a depuração, isso permite identificar com clareza as áreas de memória do Ghostty
- Também ajudou bastante a rastrear a causa do vazamento e validar a correção
- Com esse recurso, foi possível confirmar visualmente se a memória relacionada ao PageList estava sendo liberada
Sistema de prevenção de vazamentos de memória do Ghostty
- O Ghostty detecta e previne vazamentos de várias formas
- Uso do alocador com detecção de vazamentos do Zig em builds de depuração e testes unitários
- Execução de toda a suíte de testes com valgrind no CI
- Inspeção de vazamentos no código Swift com o macOS Instruments
- PRs relacionados a GTK são validados com testes de GUI no Valgrind
- Esse vazamento, porém, só ocorria sob condições específicas, então os testes existentes não conseguiram reproduzi-lo
- Um novo caso de teste foi adicionado para evitar regressões
Conclusão
- Este caso foi confirmado como o maior vazamento de memória já encontrado no Ghostty
- Mesmo após a correção, o problema continuará sendo monitorado por meio de relatos de usuários e testes de reprodução
- Os dados de diagnóstico e os casos reproduzíveis fornecidos pela comunidade tiveram papel decisivo na resolução
- Reforça-se que garantir um ambiente reproduzível é essencial para resolver vazamentos de memória
1 comentários
Comentários do Hacker News
Excelente notícia. Parabéns a todos que participaram da correção do problema
Esse bug já tinha sido mencionado neste tópico na semana passada
Parece que o Claude Code ajudou a expor esse bug para mais usuários, mas houve gente como eu que passou pelo mesmo problema mesmo sem usar Claude Code
O critério para uma página ser classificada como “não padrão (non-standard)” não é tão preto no branco quanto parece
Também acho que para quem usa configurações como
scrollback-limit = 0, o vazamento pode ter acontecido com mais frequênciaFica a sensação de que a forma como foi corrigido pode acabar apagando e recriando páginas não padrão sem necessidade, então talvez desse para reutilizar páginas antigas que já eram não padrão
O comportamento de PageList sempre foi o mesmo, e mesmo quando havia bug ele só via um tamanho incorreto durante o redimensionamento de capacidade
Não deve haver mudança perceptível de desempenho
A alternativa sugerida também foi considerada, mas a abordagem atual está bem sustentada por dados de benchmark
Posso até mudar de ideia no futuro, mas desta vez foquei em corrigir o vazamento em vez de mudar toda a visão de mundo
Era, de fato, um bug reproduzível que causava segfault
Deixa a CLI com cara de novidade mais do que qualquer coisa nos últimos 20 anos
Ótimo texto. Obrigado ao mitchellh por criar o Ghostty
Migrei no ano passado e não me arrependi nem um pouco
Só achei um pouco surpreendente que a correção vá entrar numa release de funcionalidade daqui a alguns meses
Eu esperava que entrasse numa release de correção de bugs
Assim que começou a falar de páginas, pensei “ah, é pooling de memória”, e depois “deve ser um buffer circular”, e no fim era mesmo reutilização do scrollback
Também imaginei na hora onde estava o bug — na parte que não liberava corretamente a memória das páginas
O diagrama de alinhamento de memória também ficou excelente
Isso relembra como toda tentativa nova pode introduzir possibilidades de vazamento
Mudei para o Ghostty esta semana e, enquanto desenvolvia um app de UI para terminal, tive um crash por OOM
A estrutura usava ícones UTF8 na barra de abas, e ao redimensionar o terminal o crash acontecia imediatamente
Era tão fácil de reproduzir que eu já estava preparando um bug report, mas parece muito parecido com o problema explicado no post do blog
Espero que seja resolvido
Pergunta para @mitchellh — que ferramenta foi usada para criar a visualização de memória? E, como o site funciona bem até no celular, fiquei curioso sobre a stack usada
O código da visualização era descartável, então só validei a correção dele, não a qualidade
Eu separo namespaces por post no blog e não reutilizo isso
Só confirmei que a implementação não fazia nada estranho (como minerar bitcoin, vazar segredos etc.)
O principal é transmitir a informação, e esse tipo de diagrama deixa o conteúdo muito mais fácil de entender
Continuo acompanhando o desenvolvimento do Ghostty
Passa um pouco uma sensação de overengineering, mas esse tipo de post-mortem de bug é material valiosíssimo para quem ama capricho técnico
Se fosse um terminal em Rust, fico curioso sobre como esse tipo de implementação seria feito sem perda de desempenho
Mesmo para quem não entende muito de Ghostty ou de emuladores de terminal, foi um texto fácil de acompanhar
A acessibilidade e a didática da explicação chamaram atenção
Isso reforça a importância de um bug report reproduzível
Estou esperando alguém dizer “isso não teria acontecido se fosse em Rust”
Rust não garante "segurança contra vazamento de memória (leak safety)" no nível da linguagem
Mesmo código Rust seguro pode vazar memória — só que isso não é um problema de segurança
A própria API padrão permite vazamentos de forma explícita, como em Box::leak
Rust apenas dificulta a criação de vazamentos não intencionais, mas não os impede completamente