1 points par GN⁺ 4 시간 전 | 1 commentaires | Partager sur WhatsApp
  • En compilant et en exécutant à répétition un programme C avec entr, hello s’est terminé anormalement, mais le message Segmentation fault n’est pas apparu après la sortie de compilation
  • Ce message n’est pas affiché par le programme qui a planté, mais par le shell parent qui récupère le processus enfant et constate qu’il s’est terminé avec SIGSEGV
  • Même en indiquant explicitement bash -c, s’il ne reste effectivement qu’une seule commande à exécuter, Bash peut éviter de créer un nouveau processus et se remplacer lui-même via execve, ce qui fait disparaître le shell parent chargé d’afficher le message
  • Dans un script séparé, le Bash lancé par le shebang exécute ./hello comme processus enfant et l’attend ; il constate donc le SIGSEGV et affiche Segmentation fault (core dumped)
  • En exécutant ./hello dans un sous-shell (./hello), ou en ajoutant ; true après pour laisser une tâche suivante, on empêche l’optimisation exec et on peut voir le message d’erreur

Le chemin d’exécution où le message disparaît

  • Avec la commande entr suivante, la compilation et l’exécution sont relancées chaque fois que hello.c change

    ls hello.c | entr -s "gcc -o hello hello.c && ./hello"
    
  • hello s’est terminé sur une erreur de segmentation, mais aucune sortie n’est restée visible, ni message Segmentation fault, ni état de sortie non nul perceptible

  • Même en enveloppant la commande avec bash -c, le résultat était identique

    ls hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello'"
    
  • En revanche, si l’on déplace la même commande dans run.sh et qu’on l’exécute, l’erreur s’affiche

    #!/bin/bash
    gcc -o hello hello.c && ./hello
    
    ls hello.c | entr -s ./run.sh
    ./run.sh: line 2: 104465 Segmentation fault (core dumped) ./hello
    
  • Qui affiche Segmentation fault ?

    • Le programme qui a planté est déjà terminé ; c’est le shell parent qui récupère le processus enfant et, après avoir constaté qu’il est mort à cause de SIGSEGV, affiche le message
    • Par conséquent, s’il ne reste aucun shell parent pour afficher le message, rien n’apparaît
    • Dans bash -c "some_command", si le travail à exécuter se résume effectivement à cette commande, Bash peut ne pas forker de nouveau processus et se remplacer lui-même via execve
    • Cette optimisation est invisible de l’extérieur dans la plupart des exécutions
    • Avec la méthode du script, entr exécute ./run.sh comme processus enfant, et le shebang démarre un nouveau Bash
    • Ce nouveau Bash exécute gcc, puis fork ./hello et l’attend ; il peut donc constater que l’enfant s’est terminé avec SIGSEGV et laisser un message d’erreur

Deux façons de conserver un shell parent

  • Si l’on exécute la commande qui plante dans un sous-shell, le message réapparaît
    ls hello.c | entr -s "gcc -o hello hello.c && (./hello)"
    hello.c: line 1: 106595 Segmentation fault (core dumped) ( ./hello )
    
    • Les parenthèses forcent ./hello à s’exécuter dans un sous-shell forké
    • Bash conservant le rôle de parent, il peut récupérer la fin du sous-shell par SIGSEGV et afficher le message
  • Laisser une tâche à exécuter après la commande qui plante permet aussi d’empêcher l’optimisation exec
    ls hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello; true'"
    bash: line 1: 109516 Segmentation fault (core dumped) ./hello
    
    • À cause de ; true, Bash a encore quelque chose à faire après la fin de ./hello et ne se remplace donc pas par ce programme
    • On peut ainsi voir le message d’erreur de segmentation sans script wrapper séparé

1 commentaires

 
GN⁺ 4 시간 전
Avis sur Lobste.rs
  • Les distributions Linux récentes désactivent les core dumps par défaut, ce qui est souvent pénible
    On peut les réactiver avec ulimit -c unlimited, mais le fichier core ne semble même pas être créé dans le même répertoire que l’exécutable, et systemd paraît le déplacer quelque part
    Ce serait bien, comme sur OpenBSD, d’activer les core dumps par défaut et de les placer dans le même répertoire que l’exécutable, mais aujourd’hui, quand un crash se produit, il faut en général relancer l’exécutable dans gdb

    • Avec systemd, on peut lister et récupérer les core dumps récents avec coredumpctl
    • Placer le core dump à l’emplacement de l’exécutable semble être un choix assez étrange
      On n’irait de toute façon pas chercher un core dump dans un endroit comme /usr/bin, et ce type d’emplacement se trouve souvent sur un système de fichiers de petite taille ou en lecture seule
      Comme emplacement par défaut, le répertoire de travail ne serait-il pas bien meilleur ?
    • Si les fichiers core sont désactivés par défaut, c’est apparemment parce que les experts en sécurité estiment que les utilisateurs ne savent pas exploiter Linux de manière sûre et que des fichiers core laissés à l’abandon peuvent être détournés
      On a même l’impression que, même lorsque l’utilisateur active les fichiers core, on part du principe qu’il ne sait pas ce qu’il fait, au point de les cacher dans un endroit difficile à trouver, même pour root
  • Si l’on écrit un programme en C, en D, etc., et qu’on veut connaître la cause de sa fin, il suffit de vérifier la valeur de retour de l’appel système wait pour savoir si le programme s’est terminé à cause d’un signal
    Le fait que le système d’exploitation redirige le core dump ailleurs présente aussi l’avantage de permettre un débogage immédiat en cas de besoin, ou l’envoi d’un rapport d’erreur à la distribution
    Lorsqu’il crée un core dump, le noyau Linux lit un fichier spécial nommé /proc/sys/kernel/core_pattern pour déterminer l’emplacement de sauvegarde
    Au lieu d’écrire dans un fichier, il peut aussi le transmettre par pipe à un programme, ce qui permet d’afficher une boîte de dialogue GUI, de lancer immédiatement un débogueur, ou d’envoyer un rapport par e-mail au développeur
    systemd définit le core_pattern du noyau sur une valeur qu’il gère lui-même, mais on peut utiliser la même fonctionnalité du noyau sans systemd
    On raconte aussi que, pendant le développement de Windows XP, l’envoi de core dumps à Microsoft a permis de corriger de nombreux bugs dont l’existence même était inconnue ; et sur Windows, si l’on installe les outils de développement, le débogueur à la demande est assez pratique lorsqu’on développe ses propres programmes
    Cette fonctionnalité a été introduite dans Linux en 2003, et davantage de détails sont disponibles dans man 5 core

  • Les core dumps sont excellents pour le débogage, mais lorsqu’ils sont placés à côté de fichiers apparemment sans rapport, ils perturbent les utilisateurs ordinaires, et ils peuvent être catastrophiques comme mécanisme de rapport de diagnostic
    J’ai été stupéfait la première fois que j’ai vu la quantité de données contenues dans un rapport de crash Windows soumis
    Si l’on part du principe qu’un système de rapport de crash protège la vie privée, il faut apprendre à déduire la cause à partir des seules informations de pile et de registres, afin de pouvoir diagnostiquer les crashes remontés

  • Le fait que Bash exec parfois directement des commandes à l’intérieur d’un script m’était totalement inconnu, et c’est une fonctionnalité surprenante
    La surcharge du processus Bash lui-même n’est pas énorme, et j’ai de toute façon l’habitude d’utiliser exec là où il le faut, donc cela donne l’impression d’une fonctionnalité quelque peu mal conçue
    Cela dit, en additionnant le nombre total d’exécutions, on peut imaginer que la différence devienne plus importante que prévu