Situația actuală: PHP2Shell. Două vulnerabilități critice PHP (CVSS 9.2) permit cod malițios fără autentificare. Patchuri din 21.10.2026.Ce este de făcut acum →

Stabilirea limitelor de memorie cu systemd și cgroups

Ca măsură provizorie PHP2Shell, php.net recomandă memory_limit=-1 și o limită de memorie în afara PHP. Cum stabiliți această limită cu systemd și cgroups, fără ca un singur worker să tragă după el întregul serviciu.

Stadiu 6 min de cititSecurizare și configurare

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.

  1. Urmăriți câteva zile necesarul curent de memorie al tuturor proceselor FPM, inclusiv la vârfurile de încărcare.
  2. Stabiliți necesarul tipic al unui worker și înmulțiți-l cu pm.max_children al tuturor pool-urilor serviciului.
  3. Prevedeți un adaos pentru procesul master, OPcache și vârfuri.
  4. Stabiliți MemoryHigh= puțin peste valoarea de vârf măsurată, iar MemoryMax= cu o oarecare marjă deasupra.
  5. 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.

Important

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 o OOMPolicy potrivită, blocăm memory_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.

Surse

Contact imediat

Fiecare oră contează. Scrieți-ne acum.

Descrieți pe scurt sistemul, versiunea PHP și ce ați observat. Ne vom face auziți, clarificăm situația și lucrăm la distanță până când incidentul este încheiat și documentat.

Ajutor imediat