Gdzie jest fork () na bomby Fork: () {: |: &};:?

25

Ostrzeżenie: Uruchomienie tego polecenia w większości powłok spowoduje uszkodzenie systemu, który będzie wymagał wymuszonego zamknięcia w celu naprawy

Rozumiem funkcję rekurencyjną :(){ :|: & };:i jej działanie. Ale nie wiem, gdzie jest wywołanie systemowe wideł. Nie jestem pewien, ale podejrzewam w fajce |.

mavillan
źródło
Powiązane (i warte przeczytania): Jak działa bomba widełkowa?
terdon

Odpowiedzi:

30

W wyniku wprowadzenia potoku x | ytworzona jest podpowłoka zawierająca potok jako część grupy procesów pierwszego planu. Tworzy to podpowłoki (via fork()) w nieskończoność, tworząc w ten sposób bombę widełkową.

$ 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

Jednak rozwidlenie nie występuje, dopóki kod nie zostanie uruchomiony, co jest ostatecznym wywołaniem :w kodzie.

Aby zdemontować działanie bomby widelcowej:

  • :() - zdefiniuj nową funkcję o nazwie :
  • { :|: & } - definicja funkcji, która rekurencyjnie przesyła funkcję wywołującą do innej instancji funkcji wywołującej w tle
  • : - wywołać funkcję bomby widełkowej

Zwykle nie wymaga to zbyt dużej pamięci, ale wysysa PID i zużywa cykle procesora.

Chris Down
źródło
W x | y, dlaczego jest tam sub-shell stworzył? Dla mojego zrozumienia, gdy bash widzi a pipe, wykonuje pipe()wywołanie systemowe, które zwraca dwa fds. Teraz komenda_lewa jest edytowana, execa dane wyjściowe są podawane do komendy_prawo jako dane wejściowe. Teraz Command_right jest edytowany exec. Dlaczego za BASHPIDkażdym razem jest inaczej?
Abhijeet Rastogi,
2
@shadyabhi To proste - xi ysą 2 osobne komendy działające w 2 osobnych procesach, więc masz 2 osobne podpowłoki. Jeśli xdziała w tym samym procesie co powłoka, oznacza to, że xmusi być wbudowany.
jw013,
24

Ostatni bit kodu ;:uruchamia funkcję :(){ ... }. W tym miejscu występuje widelec.

Średnik kończy pierwsze polecenie, a my rozpoczynamy kolejne, tzn. Wywołujemy funkcję :. Definicja tej funkcji obejmuje wywołanie do siebie ( :), a wynik tego wywołania jest przesyłany do wersji w tle :. To wspiera proces na czas nieokreślony.

Za każdym razem, gdy wywołanie funkcji :()jesteś wywołanie funkcji C fork(). Ostatecznie spowoduje to wyczerpanie wszystkich identyfikatorów procesów (PID) w systemie.

Przykład

Możesz zamienić się |:&czymś innym, aby dowiedzieć się, co się dzieje.

Skonfiguruj obserwatora

Zrób to w jednym oknie terminala:

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

Ustaw bombę wideł „z opóźnieniem bezpiecznika”

W innym oknie uruchomimy nieco zmodyfikowaną wersję widełkowej bomby. Ta wersja spróbuje się dusić, abyśmy mogli przestudiować, co robi. Nasza wersja będzie spała przez 61 sekund przed wywołaniem funkcji :().

Będziemy również tworzyć tło pierwszego połączenia, po jego wywołaniu. Ctrl+ z, a następnie wpisz bg.

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

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

Teraz, jeśli uruchomimy jobspolecenie w oknie początkowym, zobaczymy:

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

Po kilku minutach:

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

Zamelduj się u obserwatora

Tymczasem w drugim oknie, w którym działamy 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

Hierarchia procesów

A ps -auxfpokazuje hierarchię procesów:

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

Czas na sprzątanie

A killall bashzatrzyma rzeczy, zanim wymkną się spod kontroli. Czyszczenie w ten sposób może być trochę ciężkie, łagodniejszy i łagodniejszy sposób, który potencjalnie nie rozbije wszystkich bashpocisków, to:

  1. Określ, w jakim pseudo terminalu ma trafić widelec bomba

    $ tty
    /dev/pts/4
  2. Zabij pseudo terminal

    $ pkill -t pts/4

Więc co się dzieje?

Cóż, każde wywołanie bashi sleepjest wywołaniem funkcji C fork()z bashpowłoki, z której uruchomiono polecenie.

slm
źródło
7
bashmoże działać na osobnych terminalach. Lepiej byłoby użyć pkill -t pts/2.
Maciej Piechotka,
@MaciejPiechotka - dzięki za podpowiedź. Nigdy wcześniej tego nie widziałem, dodałem go do odpowiedzi!
slm