NFsec Logo

Trochę o ulimit oraz prlimit

19/04/2019 w Administracja Brak komentarzy.  (artykuł nr 690, ilość słów: 1548)

W

systemach operacyjnych każdy proces ma określony zestaw zasobów dostępnych w czasie jego życia. W przypadku Linuksa obejmują one takie rzeczy jak: ilość otwartych plików, rozmiar pliku z zrzutem pamięci, liczbę wątków, czy rozmiar stosu. Każdy zasób ma dwie granice: miękką (ang. soft) oraz twardą (ang. hard). Wartość graniczna zasobów może wahać się pomiędzy [miękką, a twardą] granicą. Pierwszą można uznać za wartość domyślną, a drugą za wartość maksymalnego pułapu. Zadaniem jądra jest upewnienie się, że te ograniczenia są egzekwowane.

W jaki sposób? Jądro ustawiając limity na zasoby per proces śledzi ich użycie, a nawet zabija dane procesy, gdy zachodzi taka konieczność (gdy te próbują przekroczyć twarde granice). To nasuwa pytanie: czy istnieje sposób na zmianę soft i hard limitów? Jeśli, tak to czy zwykły użytkownik jest zobligowany do tego? Jądro ma pewne reguły dotyczące zmiany limitu zasobów. Zapytanie lub ustawienie limitów zasobów procesów może być wykonane tylko przez proces wywołujący sam siebie lub przez proces, który jest jego właścicielem. Każdy inny proces chcący odpytać się lub ustawić limity zasobów innemu procesowi musi posiadać bit możliwości CAP_SYS_RESOURCE. Tak, więc:

  • Odpytanie o limity: każdy może odpytać o aktualne limity zasobów miękkich i twardych procesu, którego jest właścicielem.
  • Ustawienie limitów: Hard limit raz ustawiony nie może być ponownie podniesiony (w aktualnej sesji). Soft limit może zostać podniesiony tylko do wartości twardego limitu, a więc poruszać się w zakresie [0, hard limit]. Jeśli do zmiany limitów użyjemy polecenia ulimit (bez przełączników -S / -H) to system wewnętrznie ustawi zarówno soft, jak i hard limit, co może mieć swoje nieodwracalne konsekwencje.

Uprawnienia do ustawienia limitów zasobów przedstawiają się następująco:

  • Uprzywilejowany proces – (taki jak administrator [root] systemu lub posiadający bit CAP_SYS_RESOURCE) może zwiększać i zmniejszać miękkie i twarde limity.
  • Nieuprzywilejowany proces – może zwiększyć wartość soft limitu do wartości hard lub nieodwracalnie zmniejszyć twardy limit (raz zredukowany, może być tylko dalej zmniejszany) do wartości większej lub równej soft limitowi.

Wyjątek od reguły:Każda dobra reguła ma swój wyjątek: nieuprzywilejowany użytkownik może zmniejszyć i/lub zwiększyć rozmiar pliku z zrzutem pamięci (ang. core file size). Furtka ta została zostawiona dla programistów, aby mogli generować zrzuty pamięci i później analizować je np. za pomocą GDB.

Szybki test pokazujący, że reguły te obowiązują dla zwykłych użytkowników. Zmanipulujmy limit dotyczący ilości otwartych plików, który często jest kojarzony z błędem “Too many open files“:

agresor@darkstar:~$ ulimit -n
1024
agresor@darkstar:~$ ulimit -aS | grep 'open files'
open files                      (-n) 1024
agresor@darkstar:~$ ulimit -aH | grep 'open files'
open files                      (-n) 1048576
agresor@darkstar:~$ ulimit -n 3000
agresor@darkstar:~$ ulimit -aS | grep 'open files'
open files                      (-n) 3000
agresor@darkstar:~$ ulimit -aH | grep 'open files'
open files                      (-n) 3000
agresor@darkstar:~$ ulimit -n 3001
-bash: ulimit: open files: cannot modify limit: Operation not permitted
agresor@darkstar:~$ ulimit -n 2000
agresor@darkstar:~$ ulimit -n
2000
agresor@darkstar:~$ ulimit -aS | grep 'open files'
open files                      (-n) 2000
agresor@darkstar:~$ ulimit -aH | grep 'open files'
open files                      (-n) 2000
agresor@darkstar:~$ ulimit -n 3000
-bash: ulimit: open files: cannot modify limit: Operation not permitted
agresor@darkstar:~$

Powyższe polecenia możemy wytłumaczyć w następujący sposób:

  • Aktualny miękki limit wynosi (standardowo) 1024,
  • Miękki limit wynosi 1024, ale twardy już 1048576,
  • Poprzez użycie polecenia ulimit (bez -S / -H) zmieniliśmy obydwa limity na wartość 3000,
  • Próba podniesienia limitów do wartości 3001 kończy się niepowodzeniem,
  • Próba obniżenia limitów do wartości 2000 kończy się sukcesem,
  • Upewnienie się, że soft i hard limit posiadają wartość 2000,
  • Próba podniesienia limitów do poprzedniej wartości 3000 kończy się niepowodzeniem, ponieważ nową granicą jest [0,2000].

Przetestowanie tego samego scenariusza dla konta administratora pozostawiam Czytelnikowi jako ćwiczenie. Tradycyjnie polecenie ulimit zostało użyte do ustawienia i wyświetlenia zasobów procesu. Program ten ma jednak dwa podstawowe problemy: działa tylko z powłoką bash i co ważniejsze – nie może być używany dla dowolnego ID procesu – PID – tylko dla procesów powłoki i jej dzieci. Skoro podniesienie limitu wymaga podwyższonych uprawnień wykorzystajmy dla przykładu sudo do podbicia limitu otwartych plików z 1024 do 2000. Niestety polecenie: sudo ulimit -n 2000 nie zadziała. Dlaczego? Cóż, jeśli uruchamiamy sudo oczekuje ono, że ulimit jest plikiem binarnym i szuka go w ramach ścieżek zdefiniowanych w zmiennej $PATH. Niestety ulimit jest wbudowanym poleceniem powłoki poleceń dlatego sudo nie wykona tej komendy. Możemy za to spróbować w ten sposób:

agresor@darkstar:~$ ulimit -n
1024
agresor@darkstar:~$ sudo bash -c "ulimit -n 2000 && ulimit -n"
[sudo] password for agresor: *****
2000

prlimit

Oprócz ulimit istnieje jeszcze inny interfejs do odpytywania i ustawiania zasobów – prlimit. Jego możliwość używania istnieje od wersji jądra 2.6.36, a sama binarka dostarczana jest wraz z paczką util-linux v2.21:

agresor@darkstar:~$ prlimit
RESOURCE   DESCRIPTION                             SOFT      HARD UNITS
AS         address space limit                unlimited unlimited bytes
CORE       max core file size                         0 unlimited bytes
CPU        CPU time                           unlimited unlimited seconds
DATA       max data size                      unlimited unlimited bytes
FSIZE      max file size                      unlimited unlimited bytes
LOCKS      max number of file locks held      unlimited unlimited locks
MEMLOCK    max locked-in-memory address space  16777216  16777216 bytes
MSGQUEUE   max bytes in POSIX mqueues            819200    819200 bytes
NICE       max nice prio allowed to raise             0         0
NOFILE     max number of open files                1024   1048576 files
NPROC      max number of processes                 7719      7719 processes
RSS        max resident set size              unlimited unlimited bytes
RTPRIO     max real-time priority                     0         0
RTTIME     timeout for real-time tasks        unlimited unlimited microsecs
SIGPENDING max number of pending signals           7719      7719 signals
STACK      max stack size                       8388608 unlimited bytes

Polecenie to może zostać użyte do modyfikacji limitów zgodnie z wymaganiami już uruchomionego procesu:

agresor@darkstar:~$ ps x
  PID TTY      STAT   TIME COMMAND
 9480 ?        Ss     0:00 /lib/systemd/systemd --user
 9563 pts/0    Ss     0:00 -bash
 9574 pts/0    R+     0:00 ps x
agresor@darkstar:~$ prlimit --pid 9563 --nofile=1024:3000
agresor@darkstar:~$ prlimit --pid 9563 --nofile
RESOURCE DESCRIPTION              SOFT HARD UNITS
NOFILE   max number of open files 1024 3000 files

lub uruchomieniu programu, który je odziedziczy:

agresor@darkstar:~$ prlimit --cpu=2 tar -pcvzf archive.tar.gz archive
tar: archive.tar.gz: Wrote only 8192 of 10240 bytes
tar: Child died with signal 9
tar: Error is not recoverable: exiting now

Kiedy proces przekroczył nowe ustawienia czasu procesora (dwie sekundy w powyższym przykładzie) został zabity przez jądro systemu. Od wersji jądra 2.6.36 możemy również podglądać limity zasobów danego procesu za pomocą systemu plików proc: cat /proc/$PID/limits.

Więcej informacji: prlimit: control resource limits, man bash, man prlimit, Ograniczenia z poziomu powłoki, Bash fork() bomb – rozwidlanie procesów

Kategorie K a t e g o r i e : Administracja

Tagi T a g i : , , , ,

Komentowanie tego wpisu jest zablokowane.