Como eu encontrei um bug no GNU Tar

Postado por Chris Siebenmann , administrador de sistemas Unix da Universidade de Toronto

De tempos em tempos, algo estranho acontece no meu trabalho que me faz pensar. Mesmo que não seja imediatamente claro quais conclusões se seguem. Mencionei recentemente que encontramos um bug no GNU Tar, e a história de como isso aconteceu é um desses casos.

Para servidores de arquivos de backup, usamos Amanda e GNU Tar. Com o tempo, ocasionalmente tivemos um problema bastante raro, em que o tar ficou louco ao fazer backup do sistema de arquivos com o diretório /var/mail , produzindo uma quantidade enorme de saída. Geralmente esse processo chegava ao infinito e tinha que matar o lixão; em outros casos, ainda terminava emitindo terabytes de dados que pareciam perfeitamente compactados. Quando mais uma vez me deparei com um arquivo tar tão gigantesco, verifiquei - e descobri que ele consiste parcialmente em zero bytes, algo que a equipe de teste do tar -t realmente não gosta, depois do qual tudo volta ao normal.

(Por causa disso, eu queria saber se os bytes nulos aparecem naturalmente nas pessoas nas caixas de correio. Aconteceu que encontrar bytes nulos nos arquivos de texto não é tão simples e sim, eles estão lá).

Recentemente, mudamos o sistema de arquivos de /var/mail para os novos servidores de arquivos Linux no Ubuntu 18.04 e, portanto, passamos para uma versão mais recente e mais padrão do GNU Tar do que nas máquinas OmniOS. Esperávamos que isso resolvesse nossos problemas, mas o mesmo incidente aconteceu quase imediatamente. Desta vez, o GNU Tar trabalhou em uma máquina Ubuntu, onde estou muito familiarizado com todas as ferramentas de depuração disponíveis, então verifiquei o processo tar execução. O teste mostrou que o tar produz um fluxo interminável de read() retornando 0 bytes:

 read(6, "", 512) = 0 read(6, "", 512) = 0 [...] read(6, "", 512) = 0 write(1, "\0\0\0\0\0"..., 10240) = 10240 read(6, "", 512) = 0 [...] 

lsof referido descritor de arquivo 6 é a caixa de correio de outra pessoa.

Usando o apt-get source tar baixei o código-fonte e comecei a procurar por chamadas de sistema read() que não verificassem a conclusão do arquivo. Depois de examinar vários níveis de endereçamento indireto, encontrei um local óbvio onde parece que essa verificação foi omitida, principalmente na função sparse_dump_region do arquivo sparse.cs . E então me lembrei de algo.

Alguns meses atrás, encontramos um problema do NFS no Alpine . Enquanto trabalhava nesse bug, rastreei o processo Alpine e notei, entre outras coisas, que ele usa ftruncate() para redimensionar caixas de correio; algumas vezes as expande, criando temporariamente uma seção esparsa do arquivo até preenchê-lo e, às vezes, compactando-o. Isso pareceu coincidir com a situação atual: áreas esparsas estão conectadas e a redução do tamanho do arquivo usando ftruncate() cria uma situação em que o tar encontra inesperadamente uma conclusão do arquivo.

(Isso até explica por que o tar às vezes é restaurado; se novos e-mails chegarem repentinamente na caixa de correio posteriormente, ele retornará ao tamanho esperado e o tar não encontrará mais um encerramento inesperado de arquivos).

Eu errei um pouco no GDB com os símbolos de depuração do Ubuntu e o código-fonte do pacote tar que recebi e consegui reproduzir o erro, embora fosse um pouco diferente da minha teoria original. Acontece que sparse_dump_region não redefine as áreas esparsas do arquivo, mas redefine áreas não esparsas (é claro), e é usado para todos os arquivos (esparsos ou não) se você executar tar com o argumento --sparse . Portanto, o erro real é que, se você executar o GNU Tar com o argumento --sparse e o arquivo for compactado enquanto está sendo lido, o tar não poderá lidar corretamente com o final do arquivo recebido antes do esperado . Se o arquivo crescer novamente, o tar será restaurado.

(Exceto quando o arquivo é escasso apenas no final e compactado apenas neste local. Nesse caso, tudo está em ordem).

Eu pensei que, mesmo assim, eu poderia verificar muitos anos atrás em nossos servidores de arquivos OmniOS. Existem maneiras de rastrear as chamadas do sistema do programa e dos análogos, e eu poderia encontrar e olhar o código fonte da minha versão do GNU Tar e executá-lo com o depurador OmniOS (embora não pareçamos ter o GDB instalado lá), e assim por diante. Mas eu não fiz. Em vez disso, encolhemos os ombros e seguimos em frente. Levei para mover o sistema de arquivos no Ubuntu para que eu movesse meu dedo e descobrisse o problema.

(Não se trata apenas das ferramentas e do ambiente; assumimos automaticamente que o OmniOS possui uma versão antiga não suportada do GNU Tar, o que não faz sentido investigar, porque o problema foi certamente resolvido na versão mais recente).

PS: Provavelmente, como uma solução rápida, simplesmente --sparse Amanda de usar a --sparse tar --sparse ao fazer backup. As caixas de correio não devem ser esparsas e, se isso acontecer, ainda compactamos os backups do sistema de arquivos , para que todos esses zero bytes sejam compactados.

PPS: Não tentei relatar o bug aos desenvolvedores do GNU Tar, porque o encontrei apenas na sexta-feira e a universidade está de férias de inverno agora. Sinta-se livre para fazer isso antes de mim.

Source: https://habr.com/ru/post/pt434624/


All Articles