Fork-bomba (és harakiri) :)

Ma az alábbi scriptről lesz szó, ami nemes egyszerűséggel a fork_bomb.sh nevet kapta. Arra való, hogy padlóra vigye a Linux vagy UNIX rendszert. Ha egy szülő folyamat egy gyermek folyamatot indít, fork történik. Ez a “bomba” saját magát indítja el újra meg újra. Ez egyébként csúnya dolog ebben a formában. 🙂

gvamosi@gergo1:~$ cat fork_bomb.sh
#!/bin/sh
#
# fork test by gvamosi on 2019-09-29
############################################

var1=0
if [ -n "$1" ]; then
var1=$1
fi
echo "$0 $var1"
var1=$((var1 + 1))

while true; do
sleep 2
/bin/sh $0 $var1 & # itt történik a rekurzív hívás, a "fork"
done

Hogy hogyan lehet elindítani?

gvamosi@gergo1:~$ ./fork_bomb.sh &

És leállítani, ha még nem késő? Két másodpercre vettem a sleep-jét, hogy legyen idő beavatkozni. A Linux-unk ugyan korlátozza az egy felhasználó által nyitható processzek számát, de egy másodperces sleep-pel a notebook-omat sikerült úgy megfektetnem, hogy csak a “szentháromság” (ctrl+alt+del) segített. Ugyanis hiába zárja be az operációs rendszer a processzeket, a rekurzív fork miatt újra és újra nyílnak a kis programocskák, így egy tartós leterhelt állapot keletkezik. Ilyenkor nem, vagy csak alig reagál a számítógépünk.

gvamosi@gergo1:~$ killall fork_bomb.sh
gvamosi@gergo1:~$ killall sh

Ez a két parancs segíthet, ebben a sorrendben, ha elég gyorsak vagyunk. 🙂

Utólag nézem, hogy a “kill -9 -1” – ilyet azért root-ként (rendszergazdaként) vagy dba-ként (adatbázis-adminisztrátorként) be ne írjunk, adatvesztés történhet! – úgy kivágta a saját processz-erdőmet egy szemvillanás alatt, hogy öröm volt nézni. 🙂 Megteszi reboot helyett, még jó, hogy a chrome böngészőmben van “restore” funkció a tabfülekre. 🙂

Egyébként a rendelkezésre álló “javakról” az ulimit parancs segítségével tájékozódhatunk.

gvamosi@gergo1:~$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 30575
max locked memory (kbytes, -l) 65536
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 30575
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited

Jól látható, hogy 1024-nél több fork_bomb.sh-t nem igen indíthatunk. A rendszergazda így simán tökön tud rúgni minket, ha garázdálkodunk. Régebben egyébként használtak az adminok ún. “watchdog” démont, ami automtikusan reboot-olta a rendszert, ha nagy volt a baj. 🙂

Van még egy érdekes parancs. Ez a root-ként (rendszergazdaként) kiadott “rm -rf /“. Rákérdezés nélküli rekurzív törlés a gyökérből kiindulva. Használata szigorúan TILOS!!! Letöröl MINDENT a DISZKRŐL!!! VÉGLEGESEN!!! Ezt a parancsot csak egyszer adhatjuk ki. 😦

Init script-ek, kill -HUP, runlevel-ek, boot loader

A Linux rendszert induláskor egy ún. boot loader tölti be, helye fizikailag a rendszerben konfigurált boot-sorrendben megtalálható “merevlemez” – HDD vagy SSD -, USB stick, CD vagy DVD, illetve hasonlóak lehetnek (a jövőre nézve). Ezeken van egy ún. MBR – Master Boot Record, ahol egy apró információt tárol el a boot loader – régebben LILO, ma inkább GRUB -, tudniillik valahova “lépnie” kell, hogy “beolvassa” az operációs rendszer file-jait, így betölti a kernel-t (a “rendszermag”, leírása később), a modulokat, és belép egy előre beállított ún. runlevel-be.

Debian 10 alatt alapban az 5-ösfutási szint” van beállítva. Ez többfelhasználós, grafikus mód. A runlevel paranccsal ellenőrizhetjük. 🙂

root@gergo1:~# runlevel
N 5

Ezen a runlevel-en a “/etc/rc5.d” könyvtár tartalma hajtódik végre. Ez a könyvtár szimbolikus linkeket (hivatkozásokat) tartalmaz a “/etc/init.d” könyvtárban lévő ún. init script-ekre.

root@gergo1:~# l /etc/rc5.d/
total 0
lrwxrwxrwx 1 root root 29 Aug 28 2017 K01apache-htcacheclean -> ../init.d/apache-htcacheclean
lrwxrwxrwx 1 root root 27 Aug 26 2017 K01speech-dispatcher -> ../init.d/speech-dispatcher
lrwxrwxrwx 1 root root 15 Aug 26 2017 S01acpid -> ../init.d/acpid
lrwxrwxrwx 1 root root 17 Aug 26 2017 S01anacron -> ../init.d/anacron
lrwxrwxrwx 1 root root 17 Aug 28 2017 S01apache2 -> ../init.d/apache2
lrwxrwxrwx 1 root root 22 Aug 26 2017 S01avahi-daemon -> ../init.d/avahi-daemon
lrwxrwxrwx 1 root root 19 Aug 26 2017 S01bluetooth -> ../init.d/bluetooth
lrwxrwxrwx 1 root root 26 Aug 26 2017 S01console-setup.sh -> ../init.d/console-setup.sh
lrwxrwxrwx 1 root root 14 Aug 26 2017 S01cron -> ../init.d/cron
lrwxrwxrwx 1 root root 14 Aug 26 2017 S01dbus -> ../init.d/dbus
lrwxrwxrwx 1 root root 14 Aug 26 2017 S01gdm3 -> ../init.d/gdm3
lrwxrwxrwx 1 root root 13 Sep 4 07:07 S01gpm -> ../init.d/gpm
lrwxrwxrwx 1 root root 17 Jun 19 03:53 S01haveged -> ../init.d/haveged
lrwxrwxrwx 1 root root 18 Aug 26 2017 S01iptables -> ../init.d/iptables
lrwxrwxrwx 1 root root 20 Oct 23 2017 S01irqbalance -> ../init.d/irqbalance
lrwxrwxrwx 1 root root 18 Sep 1 2017 S01libvirtd -> ../init.d/libvirtd
lrwxrwxrwx 1 root root 24 Sep 1 2017 S01libvirt-guests -> ../init.d/libvirt-guests
lrwxrwxrwx 1 root root 23 Sep 1 2017 S01lvm2-lvmpolld -> ../init.d/lvm2-lvmpolld
lrwxrwxrwx 1 root root 19 Aug 26 2017 S01minissdpd -> ../init.d/minissdpd
lrwxrwxrwx 1 root root 15 Aug 28 2017 S01mysql -> ../init.d/mysql
lrwxrwxrwx 1 root root 25 Aug 26 2017 S01network-manager -> ../init.d/network-manager
lrwxrwxrwx 1 root root 14 Jun 7 02:39 S01nmbd -> ../init.d/nmbd
lrwxrwxrwx 1 root root 18 Jun 19 03:53 S01plymouth -> ../init.d/plymouth
lrwxrwxrwx 1 root root 15 Aug 26 2017 S01rsync -> ../init.d/rsync
lrwxrwxrwx 1 root root 17 Aug 26 2017 S01rsyslog -> ../init.d/rsyslog
lrwxrwxrwx 1 root root 21 Jun 7 02:39 S01samba-ad-dc -> ../init.d/samba-ad-dc
lrwxrwxrwx 1 root root 15 Aug 26 2017 S01saned -> ../init.d/saned
lrwxrwxrwx 1 root root 14 Jun 7 02:39 S01smbd -> ../init.d/smbd
lrwxrwxrwx 1 root root 13 Sep 15 21:42 S01ssh -> ../init.d/ssh
lrwxrwxrwx 1 root root 14 Jun 19 03:52 S01sudo -> ../init.d/sudo
lrwxrwxrwx 1 root root 29 Aug 26 2017 S01unattended-upgrades -> ../init.d/unattended-upgrades
lrwxrwxrwx 1 root root 15 Aug 26 2017 S01uuidd -> ../init.d/uuidd
lrwxrwxrwx 1 root root 18 Sep 1 2017 S01virtlogd -> ../init.d/virtlogd

Nézzünk meg egy példát. Vegyük a Secure Shell Server-t (démont). Az init script a “/etc/init.d/ssh” file-ban található. Ez egy executable – végrehajtható – shell script. Jól látható a 3 db “x” flag.

root@gergo1:~# l /etc/init.d/ssh
-rwxr-xr-x 1 root root 3939 Apr 8 11:51 /etc/init.d/ssh

Nézzük meg a tartalmából az elindításhoz releváns részt.

case "$1" in
start)
check_privsep_dir
check_for_no_start
check_dev_null
log_daemon_msg "Starting OpenBSD Secure Shell server" "sshd" || true
if start-stop-daemon --start --quiet --oknodo --chuid 0:0 --pidfile /run/sshd.pid --exec /usr/sbin/sshd -- $SSHD_OPTS; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
stop)
log_daemon_msg "Stopping OpenBSD Secure Shell server" "sshd" || true
if start-stop-daemon --stop --quiet --oknodo --pidfile /run/sshd.pid --exec /usr/sbin/sshd; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
reload|force-reload)
check_for_no_start
check_config
log_daemon_msg "Reloading OpenBSD Secure Shell server's configuration" "sshd" || true
if start-stop-daemon --stop --signal 1 --quiet --oknodo --pidfile /run/sshd.pid --exec /usr/sbin/sshd; then
log_end_msg 0 || true
else
log_end_msg 1 || true
fi
;;
..
status)
status_of_proc -p /run/sshd.pid /usr/sbin/sshd sshd && exit 0 || exit $?
;;
*)
log_action_msg "Usage: /etc/init.d/ssh {start|stop|reload|force-reload|restart|try-restart|status}" || true
exit 1
esac

Jól látható, hogy indítását, leállítását, a konfigurációs file-ok “újraolvasását”, továbbá az újraindítását – ez utóbbi nincsen itt forráskódban – mind a “/sbin/start-stop-daemon” rendszerbináris, azaz parancs végzi.

Felhívnám a figyelmet a “reload|force-reload” case ágra. Itt van egy “start-stop-daemon –stop –signal 1” rész. Ez nem más, mint egy olyan “signal” a szerverprocessznek, hogy olvassa újra a konfigurációs file-jait. Ez régebben a UNIX-okon és Linux-okon a “kill -HUP <pid>” volt. Ez a megoldás ezzel ekvivalens. 🙂

Nézzük meg a POSIX signal-okat. 🙂

First the signals described in the original POSIX.1-1990 standard.
Signal Value Action Comment
SIGHUP 1 Term Hangup detected on controlling terminal
or death of controlling process
SIGINT 2 Term Interrupt from keyboard
SIGQUIT 3 Core Quit from keyboard
SIGILL 4 Core Illegal Instruction
SIGABRT 6 Core Abort signal from abort(3)
SIGFPE 8 Core Floating point exception
SIGKILL 9 Term Kill signal
SIGSEGV 11 Core Invalid memory reference
SIGPIPE 13 Term Broken pipe: write to pipe with no
readers
SIGALRM 14 Term Timer signal from alarm(2)
SIGTERM 15 Term Termination signal
SIGUSR1 30,10,16 Term User-defined signal 1
SIGUSR2 31,12,17 Term User-defined signal 2
SIGCHLD 20,17,18 Ign Child stopped or terminated
SIGCONT 19,18,25 Cont Continue if stopped
SIGSTOP 17,19,23 Stop Stop process
SIGTSTP 18,20,24 Stop Stop typed at terminal
SIGTTIN 21,21,26 Stop Terminal input for background process
SIGTTOU 22,22,27 Stop Terminal output for background process
The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.

Bővebb – angol nyelvű – leírást az alábbiakban olvashatunk.

https://www.linux.com/news/introduction-services-runlevels-and-rcd-scripts/

Ha beírod root-ként (rendszergazdaként), hogy “runlevel 6“, akkor reboot-ol (újraindul) a Linux-od. 🙂

Ha ki szeretnéd “dobni” az összes felhasználót – és a konzolnál ülsz, hogy nehogy kizárd magad -, akkor a “runlevel 1” – single-user mode (egy felhasználós mód) – lesz a megfelelő parancs. 🙂