Dov'è il fork () sul fork bomb: () {: |: &};:?


25

Avvertenza: l'esecuzione di questo comando nella maggior parte delle shell comporterà la rottura di un sistema che dovrà essere arrestato forzatamente

Capisco la funzione ricorsiva :(){ :|: & };:e cosa fa. Ma non so dov'è la chiamata di sistema fork. Non ne sono sicuro, ma sospetto nella pipa |.


Correlati (e merita una lettura): come funziona una bomba a forcella?
terdon

Risposte:


30

Come risultato della pipe in x | y, viene creata una subshell per contenere la pipeline come parte del gruppo di processi in primo piano. Questo continua a creare subshells (via fork()) indefinitamente, creando così una bomba a forcella.

$ 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

Il fork non si verifica effettivamente fino a quando non viene eseguito il codice, che è l'invocazione finale di :nel tuo codice.

Per smontare il funzionamento della bomba a forcella:

  • :() - definisce una nuova funzione chiamata :
  • { :|: & } - una definizione di funzione che reindirizza ricorsivamente la funzione chiamante in un'altra istanza della funzione chiamante in background
  • : - chiama la funzione bomba a forcella

Questo tende a non richiedere troppo memoria, ma risucchia i PID e consuma i cicli della CPU.


In x | y, perché viene creata una sotto-shell? Per la mia comprensione, quando bash vede un pipe, esegue la pipe()chiamata di sistema, che ne restituisce due fds. Ora, command_left viene editato exece l'output viene inviato a command_right come input. Ora, command_right è execed. Quindi, perché BASHPIDogni volta è diverso?
Abhijeet Rastogi,

2
@shadyabhi È semplice - xe ysono 2 comandi separati in esecuzione in 2 processi separati, quindi hai 2 subshells separati. Se xviene eseguito nello stesso processo della shell, ciò significa che xdeve essere integrato.
jw013,

24

L'ultimo bit del codice ;:sta eseguendo la funzione :(){ ... }. Questo è dove si sta verificando il fork.

Il punto e virgola termina il primo comando e ne stiamo iniziando un altro, ovvero invocando la funzione :. La definizione di questa funzione include una chiamata a se stessa ( :) e l'output di questa chiamata viene reindirizzato a una versione in background :. Questo sostiene il processo indefinitamente.

Ogni volta che si sta chiamando la funzione :()che si sta chiamando la funzione C fork(). Alla fine ciò esaurirà tutti gli ID di processo (PID) sul sistema.

Esempio

Puoi scambiarlo |:&con qualcos'altro in modo da avere un'idea di cosa sta succedendo.

Installa un osservatore

In una finestra del terminale, procedere come segue:

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

Installare la bomba a forcella "ritardato miccia"

In un'altra finestra eseguiremo una versione leggermente modificata della bomba a forcella. Questa versione tenterà di limitare se stessa in modo da poter studiare cosa sta facendo. La nostra versione dormirà per 61 secondi prima di chiamare la funzione :().

Inoltre eseguiremo anche la chiamata iniziale, dopo che è stata invocata. Ctrl+ z, quindi digitare bg.

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

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

Ora se eseguiamo il jobscomando nella finestra iniziale vedremo questo:

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

Dopo un paio di minuti:

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

Verifica con l'osservatore

Nel frattempo nell'altra finestra dove stiamo correndo 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

Gerarchia di processo

E a ps -auxfmostra questa gerarchia di processi:

$ 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
...
...

Tempo di pulizia

A killall bashfermerà le cose prima che sfuggano di mano. Fare la tua pulizia in questo modo può essere un po 'pesante, un modo più gentile e gentile che potenzialmente non strapperà ogni bashguscio, sarebbe fare quanto segue:

  1. Determina in quale pseudo terminale verrà lanciata la bomba

    $ tty
    /dev/pts/4
  2. Uccidi lo pseudo terminale

    $ pkill -t pts/4

Quindi cosa sta succedendo?

Bene, ogni invocazione di bashed sleepè una chiamata alla funzione C fork()dalla bashshell da cui è stato eseguito il comando.


7
bashpotrebbe essere in esecuzione su terminali separati. Meglio sarebbe usare pkill -t pts/2.
Maciej Piechotka,

@MaciejPiechotka - grazie per la punta. Non l'ho mai visto prima, l'ho aggiunto alla risposta!
slm
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.