IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

fork bomb et overflow


Sujet :

C

  1. #1
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    17
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2014
    Messages : 17
    Par défaut fork bomb et overflow
    Bonjour,

    J'aurais besoin de quelques éclaircissements suite à un comportement étrange dans un de mes programmes.

    J'effectue des calculs parallèles en créant plusieurs processus fils :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    int main(void)
    {
          pid_t pid[4];
     
            ...
     
          for (i = 0; i < 4; i++) {
                pid[i] = fork();
                if (pid[i] == 0) {
                      compute(x, y, borne);
                      exit(0);
                }
         }
     
         while (wait(NULL) > 0);
         return 0;
    }
    Jusque là pas de problème.

    J'ai eu le malheur de changer la condition d'arrêt de la boucle (i < 6) sans augmenter la taille du tableau (pid[4]).
    J'ai donc bien évidemment eu un segmentation fault dans le processus père.
    Normalement les processus fils déjà créés (au nombre de 5 au moment du segfault) deviennent des zombies (du moins ne sont plus rattachés au père et continuent leurs calculs).
    Une fois la fonction 'compute()' finie, les processus sont censés rencontrer exit(0). Hors ce n'est pas le cas. J'ai des centaines de nouveaux processus qui s'exécutent sans interruption (fork bomb).

    Cela viendra-t'il de l'overflow qui "écrase" les instructions qui suivent (jusqu'au exit(0)) créant donc une boucle infinie ? (du même genre que les attaques par buffer overflow dans les chaînes de caractères) ?

    Des avis ?

  2. #2
    Expert confirmé

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Par défaut
    Non est la réponse rapide.

    Probablement pas est la réponse honnête.

    Sur 99% des systèmes, tu vas avoir un code non modifiable donc tu ne peux pas réécrire ton code avec un overflow. Tu peux par contre changer les données qui sont peut-être utilisés dans ton compute.
    Jc

    Ps: 99% étant la raison pourquoi non est la réponse rapide et probablement pas pour la réponse honnête et plus longue

  3. #3
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 830
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 830
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par Aurel-R Voir le message
    J'ai eu le malheur de changer la condition d'arrêt de la boucle (i < 6) sans augmenter la taille du tableau (pid[4]).
    J'ai donc bien évidemment eu un segmentation fault dans le processus père.
    Ben non, pas "évident". En écrivant dans une case d'un tableau dépassant sa taille prévue, tu produis un comportement indéterminé (indéterminé dans le plus pur des sens du terme imprévisible => tu ne peux donc absolument pas prévoir ce qui va se passer => ça marche/ça marche pas/ça produit un segfault/ça reboote ton PC/ça reformate ton dd/etc...).

    Citation Envoyé par Aurel-R Voir le message
    Normalement les processus fils déjà créés (au nombre de 5 au moment du segfault) deviennent des zombies (du moins ne sont plus rattachés au père et continuent leurs calculs).
    Un zombie ce n'est pas un processus non rattaché au père (d'ailleurs même la formulation "non rattaché au père" est inexacte car un processus est toujours rattaché à un père, que ce soit son père d'origine ou bien le "init" père de tous). Mais pour en revenir au zombie, un zombie c'est un processus mort mais dont le père n'est pas encore au courant de sa mort. Typiquement tu fais un fork, puis le fils se termine, ben tant que le père n'a pas appelé wait() le fils reste zombie.

    Citation Envoyé par Aurel-R Voir le message
    Cela viendra-t'il de l'overflow qui "écrase" les instructions qui suivent (jusqu'au exit(0)) créant donc une boucle infinie ? (du même genre que les attaques par buffer overflow dans les chaînes de caractères) ?

    Des avis ?
    Pas facile de présumer de ce qui se passe car on reste en "comportement indéterminé". Une hypothèse peut-être avec ce tab qui est dupliqué dans tous les fils générés ; y compris dans le fils n° 5. Donc en fait la case tab[4] hors limite est remplie à la fois par le père (qui y inscrit le pid du fils généré) et à la fois par le fils généré (qui y inscrit 0). Double écriture dans une case théoriquement inaccessible. Ou alors en écrivant dans tab[4] effectivement tu n'écris plus dans la mémoire de ton code mais dans la partie "exec" du fils. Mais bon, ça reste une hypothèse sur un comportement que d'ailleurs chez-moi je n'arrive pas à reproduire

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #include <stdio.h>
    #include <stdlib.h>
     
    int compute(int n)
    {
    	if (n == 0 || n == 1) return 1;
    	return compute(n-2) + compute(n-1);
    }
     
    int main()
    {
    	int pid[4];
    	int i;
    	for (i=0; i < 6; i++)
    	{
    		if ((pid[i]=fork()) == 0)
    		{
    			printf("i=%d, pid=%d, calcul=%d\n", i, getpid(), compute(i * 5));
    			exit(0);
    		}
    	}
     
    	while(wait(NULL) > 0);
    }

    Citation Envoyé par Aurel-R Voir le message
    J'ai eu le malheur de changer la condition d'arrêt de la boucle (i < 6) sans augmenter la taille du tableau (pid[4]).
    Ben voilà ce qui se passe quand on néglige les #define...

    PS: finalement j'arrive à reproduire le comportement en remplaçant if ((pid[i]=fork()) == 0) (ma façon d'écrire) par pid[i]=fork(); if (pid[i] == 0) (le code d'origine). Effectivement c'est un sale truc qui se passe. Même en étant root (pour bénéficier d'un nice négatif) je n'arrive pas à killer le truc. A un moment il n'en reste plus qu'un mais il repart (et j'ai tenté aussi le kill -9). Bon bref reboot de la VM...

    PS2: bon ben en fait non, je n'arrive plus à reproduire. Les joies du comportement indéterminé
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  4. #4
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    17
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2014
    Messages : 17
    Par défaut
    Ben non, pas "évident". En écrivant dans une case d'un tableau dépassant sa taille prévue, tu produis un comportement indéterminé (indéterminé dans le plus pur des sens du terme imprévisible => tu ne peux donc absolument pas prévoir ce qui va se passer => ça marche/ça marche pas/ça produit un segfault/ça reboote ton PC/ça reformate ton dd/etc...).
    En effet, j'avais oublié que lorsque l'on accède à une case en dehors du tableau, cela n'engendre pas obligatoirement un segfault (mais bien un comportement indéterminé) le tableau étant déclaré et défini dans la pile. (Les joies du C si permissif)

    PS2: bon ben en fait non, je n'arrive plus à reproduire. Les joies du comportement indéterminé
    J'en suis venu à la même conclusion après différents tests.

    Merci pour ces précisions en tout cas

  5. #5
    Expert confirmé

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Pas facile de présumer de ce qui se passe car on reste en "comportement indéterminé". Une hypothèse peut-être avec ce tab qui est dupliqué dans tous les fils générés ; y compris dans le fils n° 5. Donc en fait la case tab[4] hors limite est remplie à la fois par le père (qui y inscrit le pid du fils généré) et à la fois par le fils généré (qui y inscrit 0). Double écriture dans une case théoriquement inaccessible. Ou alors en écrivant dans tab[4] effectivement tu n'écris plus dans la mémoire de ton code mais dans la partie "exec" du fils. Mais bon, ça reste une hypothèse sur un comportement que d'ailleurs chez-moi je n'arrive pas à reproduire
    Mais non ... ;-). Comportement indéfini ne veut pas non plus dire qu'on jette par la fenêtre les conventions et la gestion du fork.

    On sait que le souci est que soit pid[4] ou pid[5] provoque un comportement indéfini. La mémoire du père et la mémoire du fils sont partagées à la lecture, séparées à la première écriture. Vu qu'on écrit en dehors du tableau, tout peut se passer mais sur une corruption de données et pas d'instructions... Pour que ce code fasse plus que 6 fork, il faut une de ces éventualités:

    1) La boucle va trop loin mais le 6 est une constante, même en compilant sans option, cela se retrouve dans une instruction directement donc cela ne peut pas venir de là
    2) Du coup, on doit supposer que soit on fait un des forks dans les fils, soit le père fait plus de fork qu'il ne faille
    a) Expliquons la possibilité père:
    - C'est assez simple: si la case mémoire de la variable i se retrouve après le tableau, il est facile à imaginer qu'on va écrire le pid dedans
    - Mais un pid est sûrement au-dessus de i, du coup, la boucle se terminerait tout de suite après
    - Donc je ne pense pas que cela soit le cas
    b) Si c'est un des fils:
    - la case pid écrite est celle de la variable i. On a écrit 0 vu que c'est le fils. Du coup, deux choses:
    - La boucle va recommencer vu que i vaut 0
    - Oui mais normalement les fils vont juste faire compute et sortir mais en fait non:
    pid[i] vallait quelque chose de non nul au moment du fork pour pid[4], du coup, on compare pid[0] maintenant et c'est non nul. Donc le fils prend le comportement du père et recommence à écrire dans sa copie de pid... et on a un comportement qui va créer des forks partout

    Donc, pour que cela soit vrai, il faut avoir le i après le tableau pid. Si c'est vrai alors mon explication (b) fonctionne et tu aurais le comportement que tu as décrit.

    Notes:
    - ceci n'arrive que quand i est en mémoire et que pid est un tableau lors de la compilation. Avec ma version de Gcc, en O1, cela disparaît entièrement. Donc je vais supposer que tu compiles sans options d'optimisations
    - dans mon cas, avec mon compilateur, le i est placé avant le tableau donc le problème ne se présente pas

  6. #6
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 830
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 830
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par fearyourself Voir le message
    - Oui mais normalement les fils vont juste faire compute et sortir mais en fait non:
    pid[i] vallait quelque chose de non nul au moment du fork pour pid[4], du coup, on compare pid[0] maintenant et c'est non nul. Donc le fils prend le comportement du père et recommence à écrire dans sa copie de pid... et on a un comportement qui va créer des forks partout
    Brillante déduction

    Citation Envoyé par fearyourself Voir le message
    - ceci n'arrive que quand i est en mémoire et que pid est un tableau lors de la compilation. Avec ma version de Gcc, en O1, cela disparaît entièrement. Donc je vais supposer que tu compiles sans options d'optimisations
    Encore exact !!!
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Fork Bomb] Présentation
    Par Fork Bomb dans le forum Présentations
    Réponses: 2
    Dernier message: 25/12/2012, 12h47
  2. [langage] [Fork] Détecter un fichier
    Par GLDavid dans le forum Langage
    Réponses: 11
    Dernier message: 08/07/2004, 01h05
  3. Stack overflow
    Par portu dans le forum Langage
    Réponses: 3
    Dernier message: 26/11/2003, 15h16
  4. [LG]floation point overflow
    Par mikoeur dans le forum Langage
    Réponses: 8
    Dernier message: 10/07/2003, 12h51
  5. Pas de fork sous Windows?
    Par chezjm dans le forum POSIX
    Réponses: 8
    Dernier message: 11/06/2002, 12h15

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo