Où est la fourche () sur la bombe fourche: () {: |: &};:?

25

Avertissement: l'exécution de cette commande dans la plupart des shells entraînera un système défectueux qui nécessitera un arrêt forcé pour être corrigé

Je comprends la fonction récursive :(){ :|: & };:et ce qu'elle fait. Mais je ne sais pas où est l'appel système fork. Je ne suis pas sûr, mais je soupçonne dans la pipe |.

mavillan
la source
Connexes (et mérite d'être lu): Comment fonctionne une bombe à fourche?
terdon

Réponses:

30

À la suite de l'entrée du tuyau x | y, un sous-shell est créé pour contenir le pipeline en tant que partie du groupe de processus de premier plan. Cela continue de créer des sous-coquilles (via fork()) indéfiniment, créant ainsi une bombe fourchette.

$ for (( i=0; i<3; i++ )); do
>     echo "$BASHPID"
> done
16907
16907
16907
$ for (( i=0; i<3; i++ )); do
>     echo "$BASHPID" | cat
> done
17195
17197
17199

Cependant, le fork ne se produit pas tant que le code n'est pas exécuté, ce qui est l'invocation finale de :dans votre code.

Pour démonter le fonctionnement de la bombe à fourche:

  • :() - définir une nouvelle fonction appelée :
  • { :|: & } - une définition de fonction qui redirige récursivement la fonction appelante dans une autre instance de la fonction appelante en arrière-plan
  • : - appelez la fonction de bombe à fourche

Cela a tendance à ne pas être trop gourmand en mémoire, mais il aspire les PID et consomme des cycles CPU.

Chris Down
la source
Dans x | y, pourquoi y a-t-il un sous-shell créé? Pour ma compréhension, quand bash voit un pipe, il exécute pipe()un appel système, qui renvoie deux fds. Maintenant, command_left est execédité et la sortie est envoyée à command_right en entrée. Maintenant, command_right est execédité. Alors, pourquoi est-ce BASHPIDdifférent à chaque fois?
Abhijeet Rastogi
2
@shadyabhi C'est simple - xet il yy a 2 commandes distinctes exécutées dans 2 processus distincts, vous avez donc 2 sous-coquilles distinctes. Si xs'exécute dans le même processus que le shell, cela signifie qu'il xdoit être intégré.
jw013
24

Le dernier bit du code, ;:exécute la fonction :(){ ... }. C'est là que la fourche se produit.

Le point-virgule termine la première commande, et nous en commençons une autre, c'est-à-dire en invoquant la fonction :. La définition de cette fonction inclut un appel à lui-même ( :) et la sortie de cet appel est dirigée vers une version en arrière-plan :. Cela soutient indéfiniment le processus.

Chaque fois que vous appelez la fonction :()que vous appelez la fonction C fork(). Finalement, cela épuisera tous les ID de processus (PID) sur le système.

Exemple

Vous pouvez échanger le |:&avec quelque chose d'autre pour vous faire une idée de ce qui se passe.

Configurer un observateur

Dans une fenêtre de terminal, procédez comme suit:

$ watch "ps -eaf|grep \"[s]leep 61\""

Configuration de la bombe à fourche "fusible retardé"

Dans une autre fenêtre, nous exécuterons une version légèrement modifiée de la bombe à fourche. Cette version tentera de se limiter afin que nous puissions étudier ce qu'elle fait. Notre version dormira pendant 61 secondes avant d'appeler la fonction :().

Nous allons également mettre en arrière-plan l'appel initial, après son appel. Ctrl+ z, puis tapez bg.

$ :(){ sleep 61; : | : & };:

# control + z
[1]+  Stopped                 sleep 61
[2] 5845
$ bg
[1]+ sleep 61 &

Maintenant, si nous exécutons la jobscommande dans la fenêtre initiale, nous verrons ceci:

$ jobs
[1]-  Running                 sleep 61 &
[2]+  Running                 : | : &

Après quelques minutes:

$ jobs
[1]-  Done                    sleep 61
[2]+  Done                    : | :

Vérifiez auprès de l'observateur

Pendant ce temps, dans l'autre fenêtre où nous courons watch:

Every 2.0s: ps -eaf|grep "[s]leep 61"                                                                                                                                             Sat Aug 31 12:48:14 2013

saml      6112  6108  0 12:47 pts/2    00:00:00 sleep 61
saml      6115  6110  0 12:47 pts/2    00:00:00 sleep 61
saml      6116  6111  0 12:47 pts/2    00:00:00 sleep 61
saml      6117  6109  0 12:47 pts/2    00:00:00 sleep 61
saml      6119  6114  0 12:47 pts/2    00:00:00 sleep 61
saml      6120  6113  0 12:47 pts/2    00:00:00 sleep 61
saml      6122  6118  0 12:47 pts/2    00:00:00 sleep 61
saml      6123  6121  0 12:47 pts/2    00:00:00 sleep 61

Hiérarchie des processus

Et un ps -auxfmontre cette hiérarchie de processus:

$ ps -auxf
saml      6245  0.0  0.0 115184  5316 pts/2    S    12:48   0:00 bash
saml      6247  0.0  0.0 100988   468 pts/2    S    12:48   0:00  \_ sleep 61
....
....
saml      6250  0.0  0.0 115184  5328 pts/2    S    12:48   0:00 bash
saml      6268  0.0  0.0 100988   468 pts/2    S    12:48   0:00  \_ sleep 61
saml      6251  0.0  0.0 115184  5320 pts/2    S    12:48   0:00 bash
saml      6272  0.0  0.0 100988   468 pts/2    S    12:48   0:00  \_ sleep 61
saml      6252  0.0  0.0 115184  5324 pts/2    S    12:48   0:00 bash
saml      6269  0.0  0.0 100988   464 pts/2    S    12:48   0:00  \_ sleep 61
...
...

Temps de nettoyage

A killall basharrêtera les choses avant qu'elles ne deviennent incontrôlables. Faire votre nettoyage de cette façon peut être un peu dur, une manière plus douce et plus douce qui ne détruira pas potentiellement chaque bashcoquille, serait de faire ce qui suit:

  1. Déterminez dans quel pseudo terminal la bombe à fourche va s'exécuter

    $ tty
    /dev/pts/4
  2. Tuez le pseudo terminal

    $ pkill -t pts/4

Alors que se passe-t-il?

Eh bien, chaque appel de bashet sleepest un appel à la fonction C à fork()partir du bashshell à partir duquel la commande a été exécutée.

slm
la source
7
bashpeut être exécuté sur des terminaux distincts. Mieux serait d'utiliser pkill -t pts/2.
Maciej Piechotka
@MaciejPiechotka - merci pour le conseil. Jamais vu celui-là auparavant, je l'ai ajouté à la réponse!
slm