lunes 6 de julio de 2009

Qué es una 'fork bomb' y como prevenirla

En términos computacionales, 'fork' es la referencia a cuando un proceso(denominado proceso padre) crea una copia de sí mismo(proceso hijo). En ambientes multihilos esto significa que el hilo en ejecución(padre) es duplicado(hijo).

Una operación 'fork' aparta un espacio de memoria para el hijo. Este último, es una copia exacta de todos los segmentos de memoria del proceso padre, y, a pesar de que se trate de una implementación semántica copy-on-write, la memoria física podría no ser asignada(p.e. ambos procesos podrían llegar a compartir los mismos segmentos de memoria física durante algún tiempo). Tanto el proceso padre como el proceso hijo poseen los mismos segmentos de código, pero se ejecutan uno independiente del otro.

Teniendo una breve idea de lo que significa 'fork' a nivel de sistema operativo, 'fork bomb' es un ataque de tipo Denegación de Servicio o DoS (Denial of Service) en inglés, que implementa la operación fork (o una funcionalidad equivalente).


Una 'fork bomb' no se trata de un virus ya que no infecta otros programas o documentos, tampoco se trata de un gusano ya que no usa los recusos de red de la máquina para esparcirse, entra más bien en la categoría de 'wabbit'. El término 'wabbit' se atribuye probablemente a la pronunciación que hace Elmer Gruñón(caricaturas de Bugs Bunny) -en la versión en inglés- queriendo decir 'rabbit', que en español significa conejo y que al igual que los conejos una 'fork bomb' tiene la habilidad de multiplicarse rápidamente y "tumbar"(incapacitar) un sistema que confía en la asunción(generalmente válida) de que los programas y procesos que puede ejecutar simultáneamente tienen un límite.

Esta crea rápidamente una gran cantidad de procesos con el fin de saturar los espacios disponibles en la lista de procesos que guarda el sistema operativo. Si esta se satura, no podrá ejecutarse otro programa hasta que termine otro proceso y aún si esto sucediera, no es muy probable que este programa se ejecute ya que cada una de las instancias de la bomba tratarán por sí mismas de ocupar cualquier espacio disponible.

El siguiente código muestra la que podría considerarse una de las 'fork bomb' más elegantes. Presentada por Jaromil como una pieza de arte de código abierto en 2002. Ejecutas los siguientes 13 caracteres en un shell tipo Unix como bash o zsh.
:(){ :|:& };:

Lo cual significa
:()    # define ':' -- lo que sea que digamos ':', haz esto:
{      # inicio de lo que dijimos que tiene que hacer ':'
  :    # carga otra copia de la función ':' en memoria...
  |    # ...lo cual será entrada para...
  :    # ...otra copia de la función ':', que tiene que cargarse en memoria
       # (por lo tanto, ':|:' sencillamente carga dos copias de ':' como sea que ':' se llame)
  &    # libera las funciones -- si matan la primera ':', todas las demás que esta inició NO          deberían auto-matarse
}      # fin de lo que hace ':'
;      # Habiendo definido ':', ahora debería...
:      # ...llamar a ':', iniciando una reacción en cadena: cada ':' iniciará dos más.

Dado que ':' es un nombre arbitrario para la función, una versión más entendible sería:
forkbomb(){ forkbomb|forkbomb & };forkbomb

Otros ejemplos.
Versión en Windows usando lenguaje batch
%0|%0

ó un ejemplo que reaccione rápidamente
:s
start %0
%0|%0
goto :s

un poema en Perl
fork while fork

en Haskell
import Control.Monad
import System.Posix.Process

forkBomb = forever $ forkProcess forkBomb

en Python
import os

while True:
os.fork()

en Ruby
loop { fork }


Prevención

Una manera de prevenirlas es limitando el número de procesos que puede manejar un usuario. Cuando un proceso intente crear uno nuevo y el usuario esté al límite de procesos permitidos, este fallará. Los administradores deben configurar este tope lo suficientemente bajo de tal manera que si todos los usuarios lanzan una bomba simultáneamente, queden suficientes recursos para evitar el desastre; lo cual es prácticamente improbable(que todos los usuarios al mismo tiempo lancen una bomba).

Los sistemas tipo Unix, usualmente tienen un límite de procesos controlado por el comando de shell ulimit. Los kernels Linux configuran y respetan la variable RLIMIT(límite de recursos) de un proceso. De igual manera tanto en Linux como en *BSD puedes editar el archivo de configuración de pam_limits: /etc/security/limits.conf y agregar la siguiente línea. Sin embargo, verifica que está habilitado ya que no todas las distribuciones de Linux vienen con pam_limits configurado por default.
<os_user> hard nproc 200

Si no tienes PAM habilitado intenta poniendo la siguiente línea en tu archivo de configuración /etc/profile.conf
ulimit -u 200


Limitar el número de procesos que un proceso puede crear no previene de una 'fork bomb' ya que se quedarán ahí a la espera de crear más y más procesos. Un sistema que distribuya los recursos por cuota compartiendo los recursos del proceso padre podría funcionar, aunque este tipo de sistemas no son muy comunes.

Fuentes
Fork bomb
How to: Prevent a fork bomb by limiting user process