Pe scurt: Pentru măsura provizorie PHP2Shell memory_limit=-1, PHP-FPM are nevoie de o limită de memorie în afara PHP. Sub systemd o stabiliți cu MemoryHigh= ca frână și MemoryMax= ca plafon strict, într-un fișier suplimentar pentru serviciul FPM. ulimit, LimitAS= sau LimitDATA= sunt, potrivit php.net, nepotrivite pentru aceasta. Verificați în plus OOMPolicy=, pentru ca un singur worker să nu încheie întregul serviciu.
O recomandare care la început surprinde
Printre măsurile provizorii publicate de php.net la 21 octombrie 2026 pentru PHP2Shell se află una care i-a făcut pe mulți administratori să se oprească: memory_limit=-1, deci nicio limită de memorie în PHP însuși. Ani la rând, contrariul a fost considerat bună practică.
O legătură cu descrierea vulnerabilității este evidentă. La CVE-2026-109618, o stare de Out-of-Memory poate întrerupe PHP în timp ce datele cererii sunt inițializate doar parțial. Măsura provizorie mută limita de memorie din PHP în sistemul de operare. php.net o leagă explicit de două condiții: valoarea trebuie să fie blocată împotriva suprascrierii, iar limita trebuie să vină prin container sau cgroup, nu prin RLIMIT_AS sau RLIMIT_DATA.
Cine aplică doar prima jumătate are un server fără nicio limită de memorie. Acest articol arată a doua jumătate. Chiar și împreună, măsura rămâne o tranziție: vulnerabilitatea este remediată abia cu PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 sau cu un backport confirmat.
cgroups explicate în două propoziții
Control Groups, pe scurt cgroups, sunt o funcție a nucleului Linux care grupează procesele și le limitează resursele. systemd creează automat pentru fiecare serviciu un cgroup propriu, iar toate procesele serviciului, la serviciul FPM deci procesul master și toți workerii, fac parte din el.
Opțiunile de memorie ale systemd stabilesc valori ale controlerului de memorie cgroup v2. O comandă scurtă arată dacă sistemul dumneavoastră folosește cgroup v2:
# "cgroup2fs" înseamnă: cgroup v2 este activ
stat -fc %T /sys/fs/cgroup/
Versiunile actuale ale distribuțiilor uzuale folosesc implicit cgroup v2. Dacă comanda arată altceva, clarificați acest lucru înainte de trecere, deoarece documentația systemd descrie vechile opțiuni v1 ca depășite.
Stabilirea systemd MemoryMax pentru PHP-FPM
Limita se pune într-un fișier suplimentar, un așa-numit drop-in. Astfel fișierul de unitate al pachetului rămâne neschimbat, iar o actualizare nu vă suprascrie setarea. Numele serviciilor diferă în funcție de distribuție; exemplul folosește Debian și Ubuntu cu PHP 8.3.
# Deschide un editor pentru 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
Opțiunile pe scurt, conform documentației systemd:
| Opțiune | Efect | Indicație pentru PHP-FPM |
|---|---|---|
MemoryHigh= |
limită de limitare: procesele sunt puternic frânate, memoria este recuperată | mărimea propriu-zisă de control, puțin sub MemoryMax |
MemoryMax= |
limită absolută: dacă nu poate fi respectată, OOM-killer devine activ în serviciu | ultima linie de apărare |
MemorySwapMax= |
limită pentru swapping | 0 împiedică serviciul să se mute în schimb în swap |
OOMPolicy= |
comportamentul după un eveniment OOM: continue, stop sau kill |
continue lasă ceilalți workeri să ruleze în continuare |
LimitAS=, LimitDATA= |
rlimits pentru fiecare proces | potrivit php.net, nepotrivite pentru această măsură |
OOMPolicy= merită atenție specială. La stop sau kill, systemd încheie după un eveniment OOM întregul serviciu, iar toate site-urile acestui serviciu FPM cad. Ce valoare se aplică fără o indicație proprie stabilește valoarea implicită a sistemului DefaultOOMPolicy=. Verificați de aceea valoarea efectivă, în loc să o presupuneți.
Apoi reporniți serviciul și blocați memory_limit în pool:
sudo systemctl restart php8.3-fpm
systemctl show php8.3-fpm -p MemoryHigh -p MemoryMax -p MemorySwapMax -p OOMPolicy -p MemoryCurrent
; Fișierul pool-ului, de ex. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_value[memory_limit] = -1
Cum funcționează blocarea descrie php_admin_value: blocarea setărilor împotriva suprascrierii.
Găsirea valorii potrivite
O limită prea strânsă produce întreruperi în funcționarea normală, una prea generoasă protejează puțin. Punctul de plecare este consumul real.
- Urmăriți câteva zile necesarul curent de memorie al tuturor proceselor FPM, inclusiv la vârfurile de încărcare.
- Stabiliți necesarul tipic al unui worker și înmulțiți-l cu
pm.max_childrenal tuturor pool-urilor serviciului. - Prevedeți un adaos pentru procesul master, OPcache și vârfuri.
- Stabiliți
MemoryHigh=puțin peste valoarea de vârf măsurată, iarMemoryMax=cu o oarecare marjă deasupra. - Reajustați valorile după o săptămână, pe baza măsurătorilor și a evenimentelor OOM.
# Memoria tuturor proceselor PHP-FPM în MB (Resident Set Size)
ps -C php-fpm8.3 -o rss= | awk '{s+=$1} END {printf "%.0f MB\n", s/1024}'
# Prezentare continuă a tuturor cgroups sortate după memorie
systemd-cgtop -m
Ar trebui să notați valoarea lui memory_limit dinainte de trecere. După actualizare puteți reveni la o limită fixă în PHP; cum o alegeți rezonabil găsiți la Setarea corectă a memory_limit.
Supraveghere: când se activează limita
O limită de memorie al cărei declanșare nu o observă nimeni este o sursă tăcută de erori. Nucleul numără evenimentele OOM pentru fiecare cgroup, iar systemd și jurnalul nucleului le consemnează.
# Contorul OOM al cgroup-ului serviciului (calea la cgroup v2)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# Mesajele OOM-killer în jurnalul nucleului
journalctl -k --since "-1 day" | grep -i "out of memory"
Dacă contorul oom_kill crește, nucleul încheie workeri. Asta poate indica o limită prea strânsă, o aplicație defectuoasă sau cereri neobișnuite. În situația actuală merită să comparați astfel de evenimente cu jurnalele de acces din același moment.
Limita de memorie este o măsură provizorie și nu înlocuiește actualizarea PHP. Planificați actualizarea în paralel și reporniți apoi toate procesele PHP. Prezentarea tuturor măsurilor provizorii se află la Măsuri imediate dacă nu puteți încă aplica patchul.
Ce se întâmplă în interiorul PHP la lipsa de memorie explică memory_limit și Out-of-Memory. La Apache cu mod_php se aplică aceeași tehnică pentru serviciul Apache, doar că PHP împarte atunci limita cu serverul web însuși.
Concluzie: nu uitați a doua jumătate
memory_limit=-1 este o măsură provizorie justificabilă doar împreună cu o limită exterioară. systemd face posibilă această limită cu câteva rânduri: MemoryHigh ca frână, MemoryMax ca plafon strict, MemorySwapMax împotriva evitării prin swap și o OOMPolicy aleasă conștient. Măsurați înainte, observați după și nu pierdeți din vedere că toate pool-urile unui serviciu își împart limita.
Mai ales însă: planificați sfârșitul măsurii. După actualizarea la o versiune PHP corectată reveniți la un memory_limit fix. Limita cgroup o puteți păstra ca protecție suplimentară împotriva proceselor care scapă de sub control.
Cum ajută PHP SOS concret?
- Măsurare: Inventariem necesarul de memorie al serviciilor dumneavoastră PHP și deducem limite potrivite.
- Implementare: Stabilim drop-in-uri cu
MemoryHigh,MemoryMaxși oOOMPolicypotrivită, blocămmemory_limitîn pool și repornim controlat serviciile. - Observare: Integrăm evenimentele OOM în supraveghere și reajustăm valorile.
- Conducerea spre patch: Prin serviciul de patchuri PHP aplicăm actualizarea și retragem apoi ordonat valorile provizorii.
Întrebări frecvente
De ce ar trebui memory_limit să fie -1? Nu este periculos?
Singur, ar fi. php.net recomandă memory_limit=-1 ca măsură provizorie explicit împreună cu o limită de memorie prin container sau cgroup. Limitarea se mută astfel din PHP în sistemul de operare. Fără această limită exterioară nu ar trebui să stabiliți valoarea.
Pot folosi în schimb ulimit sau LimitAS=?
Nu. php.net descurajează explicit RLIMIT_AS și RLIMIT_DATA, iar tocmai aceste limite le stabilesc ulimit -v, LimitAS= și LimitDATA=. Documentația systemd atrage în plus atenția că LimitAS= limitează spațiul de adrese, nu consumul de memorie.
Ce se întâmplă când PHP-FPM atinge limita?
La MemoryHigh, nucleul frânează procesele și recuperează memorie. La MemoryMax, OOM-killer devine activ în interiorul serviciului și încheie un proces, de obicei un worker mare. Dacă după aceea se oprește întregul serviciu stabilește OOMPolicy=.
Se aplică limita fiecărui pool în parte?
Nu. Toate pool-urile unui serviciu FPM împart un cgroup și, deci, o limită comună. Limite separate pentru fiecare site necesită servicii FPM sau containere separate.
Cum stabilesc limita în Docker sau Kubernetes?
Prin limitarea de memorie a containerului, de exemplu opțiunea --memory la docker run sau resources.limits.memory în Kubernetes. Ambele folosesc tot cgroups. Principiul rămâne același: limită în afara PHP, memory_limit blocat în PHP.