Comment j'ai trouvé un bug dans GNU Tar

Publié par Chris Siebenmann , administrateur de systèmes Unix à l'Université de Toronto

De temps en temps, quelque chose d'étrange se produit dans mon travail qui me fait réfléchir. Même s'il n'est pas immédiatement clair quelles conclusions s'en suivent. J'ai récemment mentionné que nous avons trouvé un bogue dans GNU Tar, et l'histoire de la façon dont cela s'est produit en est un exemple.

Pour les serveurs de fichiers de sauvegarde, nous utilisons Amanda et GNU Tar. Au fil du temps, nous avons parfois eu un problème assez rare où tar est devenu fou lors de la sauvegarde du système de fichiers avec le répertoire /var/mail , produisant une énorme quantité de sortie. Habituellement, ce processus allait à l'infini et devait tuer le dépotoir; dans d'autres cas, elle a fini par émettre des téraoctets de données qui semblaient parfaitement compressés. Quand j'ai rencontré une fois de plus un fichier tar géant, je l'ai vérifié - et j'ai découvert qu'il se compose en partie de zéro octet, ce que l'équipe de test tar -t n'aime vraiment pas, après quoi tout revient à la normale.

(Pour cette raison, je me demandais si les octets nuls apparaissent naturellement chez les personnes dans les boîtes aux lettres. Il s'est avéré que trouver des octets nuls dans les fichiers texte n'est pas si simple et oui, ils sont là).

Nous avons récemment déplacé le système de fichiers de /var/mail vers les nouveaux serveurs de fichiers Linux sous Ubuntu 18.04 et sommes donc passés à une version plus récente et plus standard de GNU Tar que sur les machines OmniOS. Nous espérions que cela résoudrait nos problèmes, mais le même incident s'est produit presque immédiatement. Cette fois, GNU Tar a travaillé sur une machine Ubuntu, où je connais bien tous les outils de débogage disponibles, j'ai donc vérifié le processus tar cours d'exécution. Le test a montré que tar produit un flux sans fin de read() retournant 0 octet:

 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 ledit descripteur de fichier 6 est la boîte aux lettres de quelqu'un d'autre.

À l'aide d' apt-get source tar j'ai téléchargé le code source et j'ai commencé à rechercher des appels système read() qui ne vérifiaient pas l'achèvement du fichier. Après avoir examiné plusieurs niveaux d'adressage indirect, j'ai trouvé un endroit évident où il semble qu'une telle vérification ait été omise, à savoir dans la fonction sparse_dump_region du fichier sparse.cs . Et puis je me suis souvenu de quelque chose.

Il y a quelques mois, nous avons rencontré un problème NFS dans Alpine . En travaillant sur ce bogue, j'ai retracé le processus Alpine et j'ai remarqué, entre autres, qu'il utilise ftruncate() pour redimensionner les boîtes aux lettres; parfois, il les développe, créant temporairement une section clairsemée du fichier jusqu'à ce qu'il le remplisse, et peut-être parfois le compressant. Cela semblait coïncider avec la situation actuelle: des zones éparses sont connectées et la réduction de la taille du fichier à l'aide de ftruncate() crée une situation où tar rencontre de manière inattendue la fin d'un fichier.

(Cela explique même pourquoi tar est parfois restauré; si un nouveau courrier arrive soudainement dans la boîte aux lettres plus tard, il revient à la taille attendue et tar ne rencontre plus de fin de fichier inattendue).

J'ai un peu gâché dans GDB avec les symboles de débogage Ubuntu et le code source du package tar que j'ai reçu, et j'ai pu reproduire l'erreur, bien qu'elle soit quelque peu différente de ma théorie d'origine. Il s'est avéré que sparse_dump_region ne réinitialise pas les zones clairsemées du fichier, mais réinitialise les zones non clairsemées (bien sûr) et est utilisé pour tous les fichiers (clairsemés ou non) si vous exécutez tar avec l'argument --sparse . Ainsi, l'erreur réelle est que si vous exécutez GNU Tar avec l'argument --sparse et que le fichier est compressé pendant sa lecture, tar ne peut pas gérer correctement la fin du fichier reçu plus tôt que prévu . Si le fichier se développe à nouveau, tar restaure.

(Sauf lorsque le fichier est rare à la fin et compressé uniquement à cet endroit. Dans ce cas, tout est en ordre).

Je pensais tout de même que je pouvais vérifier il y a de nombreuses années sur nos serveurs de fichiers OmniOS. Il existe des moyens de retracer les appels système du programme et des analogues lsof , et je pourrais trouver et regarder le code source de ma version de GNU Tar et l'exécuter avec le débogueur OmniOS (bien que nous ne semblions pas y avoir installé GDB), etc. Mais je ne l'ai pas fait. Au lieu de cela, nous avons haussé les épaules et sommes partis. Il m'a fallu déplacer le système de fichiers sous Ubuntu pour pouvoir déplacer mon doigt et comprendre le problème.

(Il ne s'agit pas seulement des outils et de l'environnement; nous supposons automatiquement qu'OmniOS possède une ancienne version non prise en charge de GNU Tar, ce qui n'a pas de sens à enquêter, car bien sûr, le problème a été résolu dans la nouvelle version).

PS: Probablement, comme solution rapide, nous interdisons simplement à Amanda d'utiliser l' --sparse tar --sparse lors de la sauvegarde. Les boîtes aux lettres ne doivent pas être rares, et si cela se produit, nous compressons toujours les sauvegardes du système de fichiers , afin que tous ces octets zéro soient bien compressés.

PPS: Je n'ai pas essayé de signaler le bogue aux développeurs de GNU Tar, car je ne l'ai trouvé que vendredi, et l'université est en vacances d'hiver maintenant. N'hésitez pas à le faire avant moi.

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


All Articles