Warum funktionieren diese Bash-Fork-Bomben anders und welche Bedeutung hat & darin?

16

Ich verstehe, wie eine normale Gabelbombe funktioniert, aber ich verstehe nicht wirklich, warum die & am Ende der gemeinsamen Bash-Gabelbombe erforderlich ist und warum sich diese Skripte anders verhalten:

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

und

:(){ : | :& }; :

Ersteres führt zu einem Anstieg der CPU-Auslastung, bevor ich zum Anmeldebildschirm zurückkehre. Letzteres führt lediglich zum Einfrieren meines Systems und zwingt mich zu einem harten Neustart. Warum das? Beide erzeugen ständig neue Prozesse. Warum verhält sich das System also anders?

Beide Skripte verhalten sich auch anders als

:(){ : | : }; :

das macht überhaupt keine probleme, obwohl ich erwartet hätte, dass sie gleich sind. Die Bash-Handbuchseite besagt, dass die Befehle in einer Pipeline bereits in einer Subshell ausgeführt werden : sollte schon ausreichen. Ich glaube und sollte die Pipeline nur in einer neuen Subshell laufen lassen, aber warum ändert sich das so sehr?

Bearbeiten: Unter Verwendung von htop und Begrenzen der Anzahl der Prozesse konnte ich feststellen, dass die erste Variante einen tatsächlichen Prozessbaum erstellt, die zweite Variante alle Prozesse auf derselben Ebene erstellt und die letzte Variante keine Prozesse zu erstellen scheint überhaupt. Das verwirrt mich noch mehr, aber vielleicht hilft es irgendwie?

Dan K.
quelle
2
Ich denke, Ihrer letzten Variante fehlt ein Semikolon::(){ : | :; }; :
Adonis

Antworten:

22

WARNUNG VERSUCHEN SIE NICHT, DIESES GERÄT AUF EINER PRODUKTIONSMASCHINE ZU LAUFEN. NUR NICHT. Warnung: Um "Bomben" auszuprobieren, stellen Sie sicher, dass sie ulimit -uverwendet werden. Lesen Sie unten [a] .

Definieren wir eine Funktion, um die PID und das Datum (Uhrzeit) zu ermitteln:

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

Eine einfache, problemlose bombFunktion für den neuen Benutzer (schützen Sie sich: lesen Sie [a] ):

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

Wenn diese Funktion aufgerufen wird, um ausgeführt zu werden, funktioniert das folgendermaßen:

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

Der Befehl datewird ausgeführt, dann wird ein "Ja" ausgegeben, eine Pause von 1 Sekunde, dann der Schließbefehl dateund schließlich die Funktion, die das Drucken einer neuen Eingabeaufforderung beendet. Nichts Besonderes.

| Rohr

Wenn wir die Funktion so aufrufen:

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:~$

Irgendwann werden zwei Befehle gestartet, die zwei enden 1 Sekunde später und dann kehrt die Eingabeaufforderung zurück.

Das ist der Grund für die Pipe |, zwei Prozesse parallel zu starten.

& Hintergrund

Wenn wir den Anruf ändern und eine Endung hinzufügen &:

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

Die Eingabeaufforderung kehrt sofort zurück (alle Aktionen werden in den Hintergrund gesendet) und die beiden Befehle werden wie zuvor ausgeführt. Bitte notieren Sie sich den Wert der "Auftragsnummer" [1], der vor der PID des Prozesses gedruckt wurde 3380. Später wird dieselbe Nummer gedruckt, um anzuzeigen, dass die Pipe beendet wurde:

[1]+  Done                    bomb | bomb

Das ist die Wirkung von &.

Das ist der Grund von &: um Prozesse schneller in Gang zu setzen.

Einfacherer Name

Wir können eine Funktion erstellen, die einfach aufgerufen wird b, um die beiden Befehle auszuführen. In drei Zeilen eingegeben:

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

Und ausgeführt als:

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

Beachten Sie, dass wir ;in der Definition von bno verwendet haben (die Zeilenumbrüche wurden zum Trennen von Elementen verwendet). Für eine Definition in einer Zeile ist es jedoch üblich, Folgendes zu verwenden ;:

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

Die meisten Leerzeichen sind auch nicht obligatorisch, wir können das Äquivalent schreiben (aber weniger klar):

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

Wir können auch a verwenden &, um das zu trennen }(und die beiden Prozesse in den Hintergrund zu stellen).

Die Bombe.

Wenn wir die Funktion dazu bringen, ihren Schwanz zu beißen (indem wir sich selbst aufrufen), erhalten wir die "Gabelbombe":

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

Und um mehr Funktionen schneller aufrufen zu können, senden Sie die Pipe in den Hintergrund.

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

Wenn wir den ersten Aufruf an die Funktion nach einem erforderlichen anhängen ;und den Namen in ändern, erhalten :wir:

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

Normalerweise geschrieben als :(){ :|:& }; :

Oder, auf lustige Weise geschrieben, mit einem anderen Namen (einem Schneemann):

☃(){ ☃|☃&};☃

Das ulimit (das Sie vor dem Ausführen festlegen sollten) bewirkt, dass die Eingabeaufforderung nach vielen Fehlern recht schnell zurückkehrt (drücken Sie die Eingabetaste, wenn die Fehlerliste angehalten wird, um die Eingabeaufforderung abzurufen).

Der Grund für die Bezeichnung "Fork Bomb" ist, dass die Shell eine Sub-Shell startet, indem sie die laufende Shell gabelt und dann exec () mit dem auszuführenden Befehl für den gegabelten Prozess aufruft.

Eine Pipe "gabelt" zwei neue Prozesse. Wenn man es bis ins Unendliche macht, entsteht eine Bombe.
Oder ein Kaninchen, wie es ursprünglich genannt wurde, weil es sich so schnell fortpflanzt.


Zeitliche Koordinierung:

  1. :(){ (:) | (:) }; time :
    Abgebrochene
    echte 0m45.627s

  2. :(){ : | :; }; time :
    Abgebrochene
    echte 0m15.283s

  3. :(){ : | :& }; time :
    real 0m00.002 s Läuft
    noch


Ihre Beispiele:

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

    Wo die zweite Schließung die )trennt, }ist eine komplexere Version von :(){ :|:;};:. Jeder Befehl in einer Pipe wird sowieso in einer Sub-Shell aufgerufen. Welches ist die Wirkung der ().

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

    Ist die schnellere Version, geschrieben ohne Leerzeichen: :(){(:)|:&};:(13 Zeichen).

  3. :(){ : | : }; : ### funktioniert in zsh aber nicht in bash.

    Hat ein Syntaxfehler (in Bash), wird vor dem Schließen ein Metazeichen benötigt },
    wie folgt :

    :(){ : | :; }; :

[a] Erstelle einen neuen sauberen Benutzer (ich rufe meinen anbize). Melden Sie sich bei diesem neuen Benutzer in einer Konsole ansudo -i -u bizeoder:

$ su - bize
Password: 
bize:~$

Überprüfen und ändern Sie das max user processesLimit:

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

Mit nur 10 Werken als nur ein einsamer neuer Benutzer: bize. Es macht es einfacher killall -u bize, die meisten (nicht alle) Bomben anzurufen und das System loszuwerden. Bitte fragen Sie nicht, welche noch funktionieren, ich werde nicht sagen. Aber trotzdem: Ist doch auf der sicheren Seite recht günstig, passe dich deinem System an .
Dies stellt sicher, dass eine "Gabelbombe" Ihr System nicht zum Einsturz bringt .

Weitere Lektüre:

Gemeinschaft
quelle