Di mana garpu () pada bom garpu: () {: |: &};:?

25

Peringatan: Menjalankan perintah ini di sebagian besar shell akan menghasilkan sistem yang rusak yang akan membutuhkan shutdown paksa untuk memperbaikinya

Saya mengerti fungsi rekursif :(){ :|: & };:dan apa fungsinya. Tapi saya tidak tahu di mana sistem panggilan fork. Saya tidak yakin, tapi saya curiga di dalam pipa |.

mavillan
sumber
Terkait (dan layak dibaca): Bagaimana cara kerja bom fork?
terdon

Jawaban:

30

Sebagai hasil dari pipa dalam x | y, subkulit dibuat untuk berisi pipa sebagai bagian dari grup proses latar depan. Ini terus membuat subkulit (via fork()) tanpa batas, sehingga menciptakan bom fork.

$ 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

Garpu tidak benar-benar muncul sampai kode dijalankan, yang merupakan doa terakhir :dalam kode Anda.

Untuk membongkar cara kerja bom fork:

  • :() - mendefinisikan fungsi baru yang disebut :
  • { :|: & } - definisi fungsi yang secara rekursif menyalurkan fungsi panggilan ke instance lain dari fungsi panggilan di latar belakang
  • : - Memanggil fungsi bom garpu

Ini cenderung tidak terlalu intensif memori, tetapi akan menyedot PID dan mengkonsumsi siklus CPU.

Chris Down
sumber
Di x | y, mengapa ada sub-shell yang dibuat? Untuk pemahaman saya, ketika bash melihat pipe, itu mengeksekusi pipe()system call, yang mengembalikan dua fds. Sekarang, command_left adalah execed dan output diumpankan ke command_right sebagai input. Sekarang, command_right sudah diedit exec. Jadi, mengapa BASHPIDberbeda setiap kali?
Abhijeet Rastogi
2
@shadyabhi Sederhana - xdan y2 perintah terpisah berjalan dalam 2 proses terpisah, sehingga Anda memiliki 2 subkulit terpisah. Jika xberjalan dalam proses yang sama dengan shell, itu berarti xharus built-in.
jw013
24

Bit terakhir dari kode, ;:menjalankan fungsinya :(){ ... }. Di sinilah garpu terjadi.

Titik koma mengakhiri perintah pertama, dan kita mulai yang lain, yaitu memanggil fungsi :. Definisi fungsi ini mencakup panggilan untuk dirinya sendiri ( :) dan output dari panggilan ini disalurkan ke versi latar belakang :. Ini menopang proses tanpa batas.

Setiap kali Anda memanggil fungsi :()Anda memanggil fungsi C fork(). Akhirnya ini akan menghabiskan semua ID proses (PID) pada sistem.

Contoh

Anda dapat bertukar |:&dengan sesuatu yang lain sehingga Anda dapat mengetahui apa yang sedang terjadi.

Siapkan pengamat

Dalam satu jendela terminal lakukan ini:

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

Siapkan bom garpu "fuse delay"

Di jendela lain kita akan menjalankan versi bom fork yang sedikit dimodifikasi. Versi ini akan berusaha mencekik dirinya sendiri sehingga kita dapat mempelajari apa yang dilakukannya. Versi kami akan tidur selama 61 detik sebelum memanggil fungsi :().

Kami juga akan melatarbelakangi panggilan awal, setelah dipanggil. Ctrl+ z, lalu ketikkan bg.

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

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

Sekarang jika kita menjalankan jobsperintah di jendela awal kita akan melihat ini:

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

Setelah beberapa menit:

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

Periksa dengan pengamat

Sementara itu di jendela lain tempat kami menjalankan 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

Hierarki proses

Dan a ps -auxfmenunjukkan hierarki proses ini:

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

Bersihkan waktu

A killall bashakan menghentikan sesuatu sebelum mereka lepas kendali. Melakukan pembersihan dengan cara ini mungkin sedikit berat, cara yang lebih lembut yang tidak berpotensi merobohkan setiap bashshell, adalah dengan melakukan hal berikut:

  1. Tentukan terminal pseudo mana yang akan dijalankan oleh bom fork

    $ tty
    /dev/pts/4
  2. Bunuh terminal semu

    $ pkill -t pts/4

Jadi apa yang terjadi?

Yah setiap doa bashdan sleepadalah panggilan ke fungsi C fork()dari bashshell dari mana perintah itu dijalankan.

slm
sumber
7
bashmungkin berjalan di terminal terpisah. Lebih baik menggunakan pkill -t pts/2.
Maciej Piechotka
@MaciejPiechotka - terima kasih atas tipnya. Belum pernah melihat itu sebelumnya, saya telah menambahkannya ke jawabannya!
slm