Perché queste bombe a bash funzionano in modo diverso e qual è il significato di & in esso?


16

Capisco come funziona una normale bomba a forcella, ma non capisco davvero perché sia ​​necessaria la e alla fine della bomba a forcella comune e perché questi script si comportino diversamente:

:(){ (:) | (:) }; :

e

:(){ : | :& }; :

Il primo provoca un picco di utilizzo della CPU prima di riportarmi alla schermata di accesso. Quest'ultimo invece causa solo il blocco del mio sistema, costringendomi a un riavvio forzato. Perché? Entrambi creano continuamente nuovi processi, quindi perché il sistema si comporta diversamente?

Entrambi gli script si comportano diversamente da

:(){ : | : }; :

che non causa alcun problema, anche se mi sarei aspettato che fossero uguali. La pagina del manuale di bash afferma che i comandi in una pipeline sono già eseguiti in una subshell, quindi sono portato a credere che: | : dovrebbe già essere sufficiente. Credo e dovrei semplicemente eseguire la pipeline in una nuova subshell, ma perché cambia così tanto?

Modifica: usando htop e limitando la quantità di processi, sono stato in grado di vedere che la prima variante crea un albero reale di processi, la seconda variante crea tutti i processi sullo stesso livello e l'ultima variante non sembra creare alcun processo affatto. Questo mi confonde ancora di più, ma forse aiuta in qualche modo?


2
penso che alla tua ultima variante manchi un punto e virgola::(){ : | :; }; :
adonis,

Risposte:


22

ATTENZIONE NON TENTARE DI ESEGUIRE QUESTO SU UNA MACCHINA DI PRODUZIONE. NON FARE. Avvertenza: per provare eventuali "bombe" assicurati che ulimit -usia in uso. Leggi sotto [a] .

Definiamo una funzione per ottenere il PID e la data (ora):

bize:~$ d(){ printf '%7s %07d %s\n' "$1" "$BASHPID" "$(date +'%H:%M:%S')"; }

Una semplice bombfunzione senza problemi per il nuovo utente (proteggiti: leggi [a] ):

bize:~$ bomb() { d START; echo "yes"; sleep 1; d END; } >&2

Quando quella funzione viene chiamata per essere eseguita funziona come questa:

bize:~$ bomb
  START 0002786 23:07:34
yes
    END 0002786 23:07:35
bize:~$

Il comando dateviene eseguito, quindi viene stampato un "sì", uno sleep per 1 secondo, quindi il comando di chiusura datee, infine, la funzione termina la stampa di un nuovo prompt dei comandi. Nulla di bello.

| tubo

Quando chiamiamo la funzione in questo modo:

bize:~$ bomb | bomb
  START 0003365 23:11:34
yes
  START 0003366 23:11:34
yes
    END 0003365 23:11:35
    END 0003366 23:11:35
bize:~$

Due comandi vengono avviati alla volta, i due terminano 1 secondo più tardi e quindi il prompt ritorna.

Questo è il motivo per cui la pipe |avvia due processi in parallelo.

& sfondo

Se cambiamo la chiamata aggiungendo un finale &:

bize:~$ bomb | bomb &
[1] 3380
bize:~$
  START 0003379 23:14:14
yes
  START 0003380 23:14:14
yes
    END 0003379 23:14:15
    END 0003380 23:14:15

Il prompt ritorna immediatamente (tutte le azioni vengono inviate in background) e i due comandi vengono eseguiti come prima. Si noti il ​​valore di "numero lavoro" [1]stampato prima del PID del processo 3380. Successivamente, verrà stampato lo stesso numero per indicare che il tubo è terminato:

[1]+  Done                    bomb | bomb

Questo è l'effetto di &.

Questo è il motivo per &: avviare rapidamente i processi.

Nome più semplice

Possiamo creare una funzione chiamata semplicemente bper eseguire i due comandi. Digitato in tre righe:

bize:~$ b(){
> bomb | bomb
> }

Ed eseguito come:

bize:~$ b
  START 0003563 23:21:10
yes
  START 0003564 23:21:10
yes
    END 0003564 23:21:11
    END 0003563 23:21:11

Nota che ;nella definizione di non abbiamo usato b(le nuove linee sono state usate per separare gli elementi). Tuttavia, per una definizione su una riga, è normale utilizzare ;, in questo modo:

bize:~$ b(){ bomb | bomb ; }

Anche la maggior parte degli spazi non è obbligatoria, possiamo scrivere l'equivalente (ma meno chiaro):

bize:~$ b(){ bomb|bomb;}

Possiamo anche usare a &per separare }(e inviare i due processi in background).

La bomba.

Se facciamo in modo che la funzione si morda la coda (chiamando se stessa), otteniamo la "bomba a forcella":

bize:~$ b(){ b|b;}       ### May look better as b(){ b | b ; } but does the same.

E per farlo chiamare più funzioni più velocemente, invia la pipe in background.

bize:~$ b(){ b|b&}       ### Usually written as b(){ b|b& }

Se aggiungiamo la prima chiamata alla funzione dopo una richiesta ;e cambiamo il nome in :otteniamo:

bize:~$ :(){ :|:&};:

Solitamente scritto come :(){ :|:& }; :

Oppure, scritto in modo divertente, con qualche altro nome (un uomo delle nevi):

☃(){ ☃|☃&};☃

L'ulimit (che avresti dovuto impostare prima di eseguirlo) farà tornare il prompt abbastanza rapidamente dopo molti errori (premi Invio quando l'elenco degli errori si interrompe per ottenere il prompt).

La ragione per cui questa viene chiamata "fork bomb" è che il modo in cui la shell avvia una sub-shell è biforcando la shell in esecuzione e quindi chiamando exec () al processo biforcato con il comando per eseguire.

Una pipe "biforcerà" due nuovi processi. Farlo all'infinito provoca una bomba.
O un coniglio come originariamente chiamato perché si riproduce così rapidamente.


Timing:

  1. :(){ (:) | (:) }; time :

    0m45.627s terminati reali

  2. :(){ : | :; }; time :

    0m15.283s terminato reale

  3. :(){ : | :& }; time :
    real 0m00.002 s
    Ancora in esecuzione


I tuoi esempi:

  1. :(){ (:) | (:) }; :

    Dove la seconda chiusura )separa }è una versione più complessa di :(){ :|:;};:. Ogni comando in una pipe viene comunque chiamato all'interno di una sotto-shell. Qual è l'effetto di ().

  2. :(){ : | :& }; :

    È la versione più veloce, scritta per non avere spazi: :(){(:)|:&};:(13 caratteri).

  3. :(){ : | : }; : ### funziona in zsh ma non in bash.

    Ha un errore di sintassi (in bash), è necessaria una metacarattere prima della chiusura },
    come questo:

    :(){ : | :; }; :

[a] Crea un nuovo utente pulito (chiamerò il miobize). Accedi a questo nuovo utente in una consolesudo -i -u bizeo:

$ su - bize
Password: 
bize:~$

Controlla e poi modifica il max user processeslimite:

bize:~$ ulimit -a           ### List all limits (I show only `-u`)
max user processes              (-u) 63931
bize:~$ ulimit -u 10        ### Low
bize:~$ ulimit -a
max user processes              (-u) 1000

Utilizzando solo 10 opere, come è solo una solitaria nuovo utente: bize. Rende più facile chiamare killall -u bizee liberare il sistema dalla maggior parte delle bombe (non tutte). Per favore, non chiedere quali funzionano ancora, non lo dirò. Ma comunque: è piuttosto basso ma per sicurezza, adattati al tuo sistema .
Ciò garantirà che una "bomba a forcella" non crollerà il sistema .

Ulteriori letture:

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.