Ukratko: Za prijelaznu mjeru za PHP2Shell memory_limit=-1 PHP-FPM treba granicu memorije izvan PHP-a. Pod systemd-om postavljate je pomoću MemoryHigh= kao kočnice i MemoryMax= kao tvrde gornje granice u dopunskoj datoteci za FPM uslugu. ulimit, LimitAS= ili LimitDATA= za to su prema php.net-u neprikladni. Provjerite i OOMPolicy= kako jedan worker ne bi prekinuo cijelu uslugu.
Preporuka koja se isprva čini začuđujućom
Među prijelaznim mjerama koje je php.net 21. listopada 2026. objavio za PHP2Shell nalazi se jedna zbog koje su mnogi administratori zastali: memory_limit=-1, dakle bez granice memorije u samom PHP-u. Godinama se suprotno smatralo dobrom praksom.
Veza s opisom ranjivosti nameće se sama. Kod CVE-2026-109618 stanje nedostatka memorije (out of memory) može prekinuti PHP dok su podaci zahtjeva tek djelomično inicijalizirani. Prijelazna mjera seli granicu memorije iz PHP-a u operacijski sustav. php.net je izričito veže uz dva uvjeta: vrijednost mora biti zaključana od nadjačavanja, a granica mora dolaziti putem kontejnera ili cgroupa, a ne putem RLIMIT_AS ili RLIMIT_DATA.
Tko provede samo prvu polovicu, ima poslužitelj bez ikakve granice memorije. Ovaj članak pokazuje drugu polovicu. Ni zajedno mjera ne prestaje biti prijelaz: ranjivost je otklonjena tek s PHP-om 8.5.12, 8.4.27, 8.3.36, 8.2.35 ili potvrđenim backportom.
cgroupe u dvije rečenice
Control Groups, skraćeno cgroupe, funkcija su Linux jezgre koja grupira procese i ograničava njihove resurse. systemd za svaku uslugu automatski izrađuje vlastitu cgroup i svi procesi usluge, kod FPM usluge dakle master i svi workeri, pripadaju joj.
Opcije memorije u systemd-u postavljaju vrijednosti kontrolera memorije cgroup v2. Koristi li Vaš sustav cgroup v2, pokazuje kratka naredba:
# "cgroup2fs" znači: cgroup v2 je aktivan
stat -fc %T /sys/fs/cgroup/
Aktualne verzije uobičajenih distribucija zadano koriste cgroup v2. Ako naredba pokaže nešto drugo, to razjasnite prije prelaska, jer dokumentacija systemd-a stare v1 opcije opisuje kao zastarjele.
Postaviti systemd MemoryMax za PHP-FPM
Granica ide u dopunsku datoteku, takozvani drop-in. Tako unit datoteka paketa ostaje nepromijenjena i ažuriranje ne prepisuje Vašu postavku. Nazivi usluga razlikuju se po distribuciji; primjer koristi Debian i Ubuntu s PHP-om 8.3.
# Otvara uređivač za drop-in: sudo systemctl edit php8.3-fpm
# Rezultat: /etc/systemd/system/php8.3-fpm.service.d/override.conf
[Service]
MemoryAccounting=yes
MemoryHigh=1536M
MemoryMax=2G
MemorySwapMax=0
OOMPolicy=continue
Opcije ukratko, prema dokumentaciji systemd-a:
| Opcija | Učinak | Napomena za PHP-FPM |
|---|---|---|
MemoryHigh= |
granica prigušenja: procesi se snažno usporavaju, memorija se vraća | stvarna upravljačka veličina, malo ispod MemoryMax |
MemoryMax= |
apsolutna granica: ako se ne može održati, u usluzi postaje aktivan OOM killer | posljednja linija obrane |
MemorySwapMax= |
granica za izmještanje | 0 sprječava da usluga umjesto toga pobjegne u swap |
OOMPolicy= |
ponašanje nakon OOM događaja: continue, stop ili kill |
continue ostavlja preostale workere da rade |
LimitAS=, LimitDATA= |
rlimiti po procesu | prema php.net-u neprikladni za ovu mjeru |
OOMPolicy= zaslužuje osobitu pažnju. Kod stop ili kill systemd nakon OOM događaja prekida cijelu uslugu i sve web-stranice te FPM usluge ispadaju. Koja vrijednost vrijedi bez vlastitog navoda, određuje zadana vrijednost sustava DefaultOOMPolicy=. Zato provjerite djelotvornu vrijednost umjesto da je pretpostavite.
Zatim ponovno pokrenite uslugu i u poolu zaključajte memory_limit:
sudo systemctl restart php8.3-fpm
systemctl show php8.3-fpm -p MemoryHigh -p MemoryMax -p MemorySwapMax -p OOMPolicy -p MemoryCurrent
; Datoteka poola, npr. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_value[memory_limit] = -1
Kako funkcionira zaključavanje, opisuje članak php_admin_value: zaključati postavke od nadjačavanja.
Pronaći pravu vrijednost
Preuska granica izaziva prekide u redovitom radu, preširoka jedva štiti. Polazište je stvarna potrošnja.
- Nekoliko dana promatrati trenutačnu potrebu za memorijom svih FPM procesa, i u vršnim opterećenjima.
- Utvrditi tipičnu potrebu jednog workera i pomnožiti je s
pm.max_childrensvih poolova usluge. - Predvidjeti dodatak za master proces, OPcache i vrhove.
MemoryHigh=postaviti malo iznad izmjerenog vrha,MemoryMax=s malim razmakom iznad toga.- Nakon tjedan dana vrijednosti dotjerati prema mjerenjima i OOM događajima.
# Memorija svih PHP-FPM procesa u MB (Resident Set Size)
ps -C php-fpm8.3 -o rss= | awk '{s+=$1} END {printf "%.0f MB\n", s/1024}'
# Tekući pregled svih cgroupa poredanih po memoriji
systemd-cgtop -m
Vrijednost za memory_limit prije prelaska trebali biste zabilježiti. Nakon ažuriranja možete se vratiti na čvrstu granicu u PHP-u; kako je smisleno odabrati, piše u članku Pravilno postaviti memory_limit.
Nadzirati: kada granica djeluje
Granica memorije čije aktiviranje nitko ne primijeti tihi je izvor pogrešaka. Jezgra broji OOM događaje po cgroupi, a systemd i zapisnik jezgre bilježe ih.
# OOM brojač cgroupe usluge (putanja kod cgroup v2)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# Poruke OOM killera u zapisniku jezgre
journalctl -k --since "-1 day" | grep -i "out of memory"
Ako brojač oom_kill raste, jezgra prekida workere. To može upućivati na preusku granicu, na pogrešnu aplikaciju ili na neobične zahtjeve. U aktualnoj situaciji isplati se takve događaje uskladiti s pristupnim zapisnicima iz istog vremena.
Granica memorije je prijelazna mjera i ne zamjenjuje PHP ažuriranje. Planirajte ažuriranje paralelno i nakon njega ponovno pokrenite sve PHP procese. Sve prijelazne mjere ukratko nalaze se na stranici Hitne mjere ako još ne možete zakrpati.
Što se unutar PHP-a događa pri nedostatku memorije, objašnjava memory_limit i Out-of-Memory. Kod Apachea s mod_php-om ista tehnika vrijedi za Apache uslugu, ali PHP tada granicu dijeli sa samim web-poslužiteljem.
Zaključak: ne zaboraviti drugu polovicu
memory_limit=-1 opravdana je prijelazna mjera samo zajedno s vanjskom granicom. systemd tu granicu omogućuje s nekoliko redaka: MemoryHigh kao kočnicu, MemoryMax kao tvrdu gornju granicu, MemorySwapMax protiv bijega u swap i svjesno odabran OOMPolicy. Izmjerite prije, promatrajte poslije i imajte na umu da svi poolovi jedne usluge dijele granicu.
Ponajprije pak: planirajte kraj mjere. Nakon ažuriranja na ispravljenu verziju PHP-a vraćate se na čvrst memory_limit. cgroup granicu možete zadržati kao dodatno osiguranje od otrgnutih procesa.
Kako PHP SOS konkretno pomaže?
- Mjeriti: bilježimo potrebe za memorijom Vaših PHP usluga i iz toga izvodimo prikladne granice.
- Provesti: postavljamo drop-inove s
MemoryHigh,MemoryMaxi prikladnimOOMPolicy, zaključavamomemory_limitu poolu i kontrolirano ponovno pokrećemo usluge. - Promatrati: OOM događaje uključujemo u nadzor i dotjerujemo vrijednosti.
- Dovesti do zakrpe: putem PHP Patch usluge provodimo ažuriranje i nakon toga uredno povlačimo prijelazne vrijednosti.
Česta pitanja
Zašto memory_limit treba biti -1? Nije li to opasno?
Samo za sebe bilo bi. php.net memory_limit=-1 kao prijelaznu mjeru izričito preporučuje zajedno s granicom memorije putem kontejnera ili cgroupa. Ograničenje se time iz PHP-a seli u operacijski sustav. Bez te vanjske granice ne biste smjeli postaviti tu vrijednost.
Mogu li umjesto toga koristiti ulimit ili LimitAS=?
Ne. php.net izričito odgovara od RLIMIT_AS i RLIMIT_DATA, a upravo te granice postavljaju ulimit -v, LimitAS= i LimitDATA=. Dokumentacija systemd-a uz to upozorava da LimitAS= ograničava adresni prostor, a ne potrošnju memorije.
Što se događa kad PHP-FPM dosegne granicu?
Kod MemoryHigh jezgra usporava procese i vraća memoriju. Kod MemoryMax unutar usluge postaje aktivan OOM killer i prekida jedan proces, najčešće velikog workera. Zaustavlja li se nakon toga cijela usluga, određuje OOMPolicy=.
Vrijedi li granica za svaki pool zasebno?
Ne. Svi poolovi jedne FPM usluge dijele jednu cgroup i time zajedničku granicu. Odvojene granice po web-stranici traže odvojene FPM usluge ili kontejnere.
Kako postaviti granicu u Dockeru ili Kubernetesu?
Putem ograničenja memorije kontejnera, primjerice opcijom --memory kod docker run ili resources.limits.memory u Kubernetesu. Oba također koriste cgroupe. Načelo ostaje isto: granica izvan PHP-a, memory_limit u PHP-u zaključan.