Stručne: Pre prechodné opatrenie k PHP2Shell memory_limit=-1 potrebuje PHP-FPM pamäťovú hranicu mimo PHP. Pod systemd ju nastavíte pomocou MemoryHigh= ako brzdy a MemoryMax= ako tvrdej hornej hranice v doplňujúcom súbore pre službu FPM. ulimit, LimitAS= alebo LimitDATA= sú na to podľa php.net nevhodné. Skontrolujte aj OOMPolicy=, aby jeden worker neukončil celú službu.
Odporúčanie, ktoré najprv prekvapí
Medzi prechodnými opatreniami, ktoré php.net zverejnil 21. októbra 2026 k PHP2Shell, je jedno, nad ktorým mnohí administrátori zastali: memory_limit=-1, teda žiadna pamäťová hranica v samotnom PHP. Roky platilo za dobrú prax opak.
Súvislosť s opisom zraniteľnosti je zrejmá. Pri CVE-2026-109618 môže stav nedostatku pamäte (out-of-memory) prerušiť PHP v čase, keď sú údaje požiadavky inicializované len čiastočne. Prechodné opatrenie presúva pamäťovú hranicu z PHP do operačného systému. php.net ho výslovne spája s dvoma podmienkami: hodnota musí byť zablokovaná proti prepísaniu a hranica musí pochádzať z kontajnera alebo cgroup, nie z RLIMIT_AS alebo RLIMIT_DATA.
Kto zavedie len prvú polovicu, má server bez akejkoľvek pamäťovej hranice. Tento príspevok ukazuje druhú polovicu. Ani spolu opatrenie nepresahuje prechod: zraniteľnosť je opravená až s PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 alebo potvrdeným backportom.
cgroups v dvoch vetách
Control Groups, skrátene cgroups, sú funkcia jadra Linuxu, ktorá zoskupuje procesy a obmedzuje ich zdroje. systemd vytvára pre každú službu automaticky vlastnú cgroup a patria do nej všetky procesy služby, pri službe FPM teda master a všetci workeri.
Pamäťové voľby systemd nastavujú hodnoty pamäťového kontroléra cgroup v2. Či váš systém používa cgroup v2, ukáže krátky príkaz:
# "cgroup2fs" znamená: cgroup v2 je aktívna
stat -fc %T /sys/fs/cgroup/
Aktuálne verzie bežných distribúcií používajú cgroup v2 štandardne. Ak príkaz ukáže niečo iné, vyjasnite to pred prechodom, pretože dokumentácia systemd opisuje staré voľby v1 ako zastarané.
Nastavenie systemd MemoryMax pre PHP-FPM
Hranica patrí do doplňujúceho súboru, takzvaného drop-in. Súbor unit balíka tak zostane nezmenený a aktualizácia vaše nastavenie neprepíše. Názvy služieb sa podľa distribúcie líšia; príklad používa Debian a Ubuntu s PHP 8.3.
# Otvorí editor pre drop-in: sudo systemctl edit php8.3-fpm
# Výsledok: /etc/systemd/system/php8.3-fpm.service.d/override.conf
[Service]
MemoryAccounting=yes
MemoryHigh=1536M
MemoryMax=2G
MemorySwapMax=0
OOMPolicy=continue
Voľby v prehľade, podľa dokumentácie systemd:
| Voľba | Účinok | Poznámka pre PHP-FPM |
|---|---|---|
MemoryHigh= |
hranica tlmenia: procesy sa silne spomalia, pamäť sa získava späť | vlastná riadiaca veličina, o niečo pod MemoryMax |
MemoryMax= |
absolútna hranica: ak sa nedá dodržať, aktivuje sa OOM-killer v službe | posledná obranná línia |
MemorySwapMax= |
hranica pre odkladanie | 0 bráni tomu, aby služba namiesto toho unikla do swapu |
OOMPolicy= |
správanie po udalosti OOM: continue, stop alebo kill |
continue nechá ostatných workerov bežať |
LimitAS=, LimitDATA= |
rlimity na proces | podľa php.net pre toto opatrenie nevhodné |
OOMPolicy= si zaslúži osobitnú pozornosť. Pri stop alebo kill systemd po udalosti OOM ukončí celú službu a vypadnú všetky weby tejto služby FPM. Ktorá hodnota platí bez vlastného údaja, určuje systémový štandard DefaultOOMPolicy=. Účinnú hodnotu preto overte, nepredpokladajte ju.
Potom službu reštartujte a v poole zablokujte memory_limit:
sudo systemctl restart php8.3-fpm
systemctl show php8.3-fpm -p MemoryHigh -p MemoryMax -p MemorySwapMax -p OOMPolicy -p MemoryCurrent
; Súbor poolu, napr. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_value[memory_limit] = -1
Ako blokovanie funguje, opisuje php_admin_value: zablokovanie nastavení proti prepísaniu.
Nájdenie správnej hodnoty
Príliš tesná hranica vyvoláva prerušenia v bežnej prevádzke, príliš štedrá sotva chráni. Východiskom je skutočná spotreba.
- Sledovať aktuálnu potrebu pamäte všetkých procesov FPM niekoľko dní, aj pri špičkách zaťaženia.
- Zistiť typickú potrebu jedného workera a vynásobiť ju
pm.max_childrenvšetkých poolov služby. - Započítať prirážku pre master proces, OPcache a špičky.
MemoryHigh=nastaviť tesne nad nameranú špičkovú hodnotu,MemoryMax=s rezervou nad ňu.- Hodnoty po týždni dolaďiť podľa meraní a udalostí OOM.
# Pamäť všetkých procesov PHP-FPM v MB (Resident Set Size)
ps -C php-fpm8.3 -o rss= | awk '{s+=$1} END {printf "%.0f MB\n", s/1024}'
# Priebežný prehľad všetkých cgroups zoradený podľa pamäte
systemd-cgtop -m
Hodnotu memory_limit si pred prechodom zapíšte. Po aktualizácii sa môžete vrátiť k pevnej hranici v PHP; ako ju zmysluplne zvoliť, uvádza Správne nastavenie memory_limit.
Sledovanie: kedy hranica zaberie
Pamäťová hranica, ktorej spustenia si nikto nevšimne, je tichým zdrojom chýb. Jadro počíta udalosti OOM pre každú cgroup a systemd aj protokol jadra ich zaznamenávajú.
# Počítadlo OOM cgroup služby (cesta pri cgroup v2)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# Hlásenia OOM-killera v protokole jadra
journalctl -k --since "-1 day" | grep -i "out of memory"
Ak počítadlo oom_kill rastie, jadro ukončuje workerov. To môže poukazovať na príliš tesnú hranicu, na chybnú aplikáciu alebo na nezvyčajné požiadavky. V aktuálnej situácii sa oplatí také udalosti porovnať s prístupovými protokolmi z rovnakého času.
Pamäťová hranica je prechodné opatrenie a nenahrádza aktualizáciu PHP. Plánujte aktualizáciu paralelne a potom reštartujte všetky procesy PHP. Všetky prechodné opatrenia v prehľade sú pri Okamžité opatrenia, ak ešte nemôžete záplatovať.
Čo sa vo vnútri PHP deje pri nedostatku pamäte, vysvetľuje memory_limit a Out-of-Memory. Pri Apache s mod_php platí rovnaká technika pre službu Apache, PHP však potom zdieľa hranicu so samotným webovým serverom.
Záver: nezabudnúť na druhú polovicu
memory_limit=-1 je prijateľným prechodným opatrením len spolu s vonkajšou hranicou. systemd túto hranicu umožňuje niekoľkými riadkami: MemoryHigh ako brzdu, MemoryMax ako tvrdú hornú hranicu, MemorySwapMax proti úniku do swapu a vedome zvolenú OOMPolicy. Najprv merajte, potom sledujte a majte na pamäti, že všetky pooly jednej služby zdieľajú hranicu.
Predovšetkým však: naplánujte koniec opatrenia. Po aktualizácii na opravenú verziu PHP sa vrátite k pevnému memory_limit. Hranicu cgroup si môžete ponechať ako dodatočné zabezpečenie proti rozbujneným procesom.
Ako konkrétne pomáha PHP SOS?
- Merať: zachytíme pamäťovú potrebu vašich služieb PHP a odvodíme z nej vhodné hranice.
- Zaviesť: nastavíme drop-iny s
MemoryHigh,MemoryMaxa vhodnouOOMPolicy, zablokujemememory_limitv poole a služby riadene reštartujeme. - Sledovať: udalosti OOM zapojíme do monitoringu a hodnoty dolaďujeme.
- Doviesť k záplate: cez službu záplat PHP nasadíme aktualizáciu a prechodné hodnoty potom usporiadane vrátime.
Časté otázky
Prečo má byť memory_limit na -1? Nie je to nebezpečné?
Samo osebe by bolo. php.net odporúča memory_limit=-1 ako prechodné opatrenie výslovne spolu s pamäťovou hranicou cez kontajner alebo cgroup. Obmedzenie sa tak presúva z PHP do operačného systému. Bez tejto vonkajšej hranice by ste hodnotu nastavovať nemali.
Môžem namiesto toho použiť ulimit alebo LimitAS=?
Nie. php.net výslovne odrádza od RLIMIT_AS a RLIMIT_DATA a práve tieto hranice nastavujú ulimit -v, LimitAS= a LimitDATA=. Dokumentácia systemd navyše upozorňuje, že LimitAS= obmedzuje adresový priestor, nie spotrebu pamäte.
Čo sa stane, keď PHP-FPM dosiahne hranicu?
Pri MemoryHigh jadro procesy spomalí a získa pamäť späť. Pri MemoryMax sa vo vnútri služby aktivuje OOM-killer a ukončí proces, spravidla veľký worker. Či sa potom zastaví celá služba, určuje OOMPolicy=.
Platí hranica pre každý pool osobitne?
Nie. Všetky pooly jednej služby FPM zdieľajú jednu cgroup a teda spoločnú hranicu. Oddelené hranice pre každý web vyžadujú oddelené služby FPM alebo kontajnery.
Ako hranicu nastavím v Dockeri alebo Kubernetes?
Cez obmedzenie pamäte kontajnera, napríklad voľbu --memory pri docker run alebo resources.limits.memory v Kubernetes. Obe takisto využívajú cgroups. Zásada ostáva rovnaká: hranica mimo PHP, memory_limit v PHP zablokované.