Aktuālā situācija: PHP2Shell. Divas kritiskas PHP ievainojamības (CVSS 9.2) ļauj izpildīt ļaunprātīgu kodu bez pieteikšanās. Ielāpi kopš 21.10.2026.Kas jādara tagad →

Atmiņas robežu iestatīšana ar systemd un cgroups

php.net kā PHP2Shell pārejas pasākumu iesaka memory_limit=-1 un atmiņas robežu ārpus PHP. Kā šo robežu iestatīt ar systemd un cgroups, lai viens worker nenorautu līdzi visu pakalpojumu.

Aktuāls uz 5 min lasīšanas laiksNostiprināšana un konfigurācija

Īsi sakot: PHP2Shell pārejas pasākumam memory_limit=-1 PHP-FPM vajadzīga atmiņas robeža ārpus PHP. Zem systemd to iestatāt ar MemoryHigh= kā bremzi un MemoryMax= kā stingru augšējo robežu FPM pakalpojuma papildinājuma failā. ulimit, LimitAS= vai LimitDATA= šim nolūkam saskaņā ar php.net nav piemēroti. Turklāt pārbaudiet OOMPolicy=, lai viens worker nepārtrauktu visu pakalpojumu.

Ieteikums, kas sākumā izbrīna

Starp pārejas pasākumiem, ko php.net 2026. gada 21. oktobrī publicēja par PHP2Shell, ir viens, kas daudziem administratoriem lika apstāties: memory_limit=-1, tātad bez atmiņas robežas pašā PHP. Gadiem pretējais tika uzskatīts par labu praksi.

Saistība ar ievainojamības aprakstu ir acīmredzama. CVE-2026-109618 gadījumā Out-of-Memory stāvoklis var pārtraukt PHP brīdī, kad pieprasījuma dati ir tikai daļēji inicializēti. Pārejas pasākums atmiņas robežu no PHP pārceļ uz operētājsistēmu. php.net to nepārprotami saista ar diviem nosacījumiem: vērtībai jābūt nobloķētai pret pārrakstīšanu, un robežai jānāk no konteinera vai cgroup, nevis no RLIMIT_AS vai RLIMIT_DATA.

Kas īsteno tikai pirmo pusi, tam ir serveris bez jebkādas atmiņas robežas. Šis raksts rāda otro pusi. Arī kopā pasākums paliek pārejas risinājums: ievainojamība ir novērsta tikai ar PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 vai apstiprinātu backport.

cgroups divos teikumos

Control Groups, īsi cgroups, ir Linux kodola funkcija, kas grupē procesus un ierobežo to resursus. systemd katram pakalpojumam automātiski izveido savu cgroup, un visi pakalpojuma procesi, FPM pakalpojuma gadījumā master un visi worker, tai pieder.

systemd atmiņas opcijas iestata cgroup v2 atmiņas kontrollera vērtības. Vai jūsu sistēma izmanto cgroup v2, parāda īsa komanda:

# "cgroup2fs" nozīmē: cgroup v2 ir aktīvs
stat -fc %T /sys/fs/cgroup/

Aktuālās populāro distributīvu versijas pēc noklusējuma izmanto cgroup v2. Ja komanda rāda ko citu, noskaidrojiet to pirms pārejas, jo systemd dokumentācija vecās v1 opcijas apraksta kā novecojušas.

systemd MemoryMax PHP-FPM iestatīšana

Robeža nonāk papildinājuma failā, tā sauktajā drop-in. Tā pakotnes Unit fails paliek nemainīts, un atjauninājums jūsu iestatījumu nepārraksta. Pakalpojumu nosaukumi atšķiras pa distributīviem; piemērā izmantots Debian un Ubuntu ar PHP 8.3.

# Atver redaktoru drop-in failam: sudo systemctl edit php8.3-fpm
# Rezultāts: /etc/systemd/system/php8.3-fpm.service.d/override.conf
[Service]
MemoryAccounting=yes
MemoryHigh=1536M
MemoryMax=2G
MemorySwapMax=0
OOMPolicy=continue

Opcijas pārskatā, pēc systemd dokumentācijas:

Opcija Ietekme Norāde PHP-FPM
MemoryHigh= droseles robeža: procesi tiek stipri bremzēti, atmiņa tiek atgūta patiesā vadības lieluma, nedaudz zem MemoryMax
MemoryMax= absolūta robeža: ja to nevar noturēt, pakalpojumā kļūst aktīvs OOM-killer pēdējā aizsardzības līnija
MemorySwapMax= robeža izlādei 0 novērš, ka pakalpojums tā vietā izvairās uz swap
OOMPolicy= uzvedība pēc OOM notikuma: continue, stop vai kill continue ļauj pārējiem worker turpināt darbu
LimitAS=, LimitDATA= rlimits katram procesam saskaņā ar php.net šim pasākumam nepiemēroti

OOMPolicy= pelna īpašu uzmanību. Ar stop vai kill systemd pēc OOM notikuma pārtrauc visu pakalpojumu, un visas šī FPM pakalpojuma vietnes atsakās. Kāda vērtība ir spēkā bez savas norādes, nosaka sistēmas noklusējums DefaultOOMPolicy=. Tāpēc spēkā esošo vērtību pārbaudiet, nevis pieņemiet.

Pēc tam pakalpojumu pārstartējiet un pūlā nobloķējiet memory_limit:

sudo systemctl restart php8.3-fpm
systemctl show php8.3-fpm -p MemoryHigh -p MemoryMax -p MemorySwapMax -p OOMPolicy -p MemoryCurrent
; Pūla fails, piem., /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_value[memory_limit] = -1

Kā darbojas bloķēšana, apraksta php_admin_value: iestatījumu bloķēšana pret pārrakstīšanu.

Pareizās vērtības atrašana

Pārāk šaura robeža rada pārtraukumus ikdienas darbībā, pārāk devīga gandrīz neaizsargā. Sākumpunkts ir faktiskais patēriņš.

  1. Dažas dienas novērot visu FPM procesu pašreizējo atmiņas vajadzību, arī slodzes pīķos.
  2. Noskaidrot tipisko viena worker vajadzību un reizināt ar visu pakalpojuma pūlu pm.max_children.
  3. Ieplānot piemaksu master procesam, OPcache un pīķiem.
  4. MemoryHigh= iestatīt tikai virs izmērītās maksimālās vērtības, MemoryMax= ar nelielu atstarpi virs tās.
  5. Vērtības pēc nedēļas pielāgot, balstoties uz mērījumiem un OOM notikumiem.
# Visu PHP-FPM procesu atmiņa MB (Resident Set Size)
ps -C php-fpm8.3 -o rss= | awk '{s+=$1} END {printf "%.0f MB\n", s/1024}'
# Dzīva visu cgroups pārskats, sakārtots pēc atmiņas
systemd-cgtop -m

memory_limit vērtību pirms pārejas vajadzētu pierakstīt. Pēc atjauninājuma varat atgriezties pie stingras robežas PHP; kā to saprātīgi izvēlēties, rakstīts sadaļā memory_limit pareizi iestatīt.

Uzraudzība: kad robeža iedarbojas

Atmiņas robeža, kuras iedarbošanos neviens nepamana, ir kluss kļūdu avots. Kodols skaita OOM notikumus katrai cgroup, un systemd kā arī kodola žurnāls tos atzīmē.

# Pakalpojuma cgroup OOM skaitītājs (ceļš cgroup v2 gadījumā)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# OOM-killer ziņojumi kodola žurnālā
journalctl -k --since "-1 day" | grep -i "out of memory"

Ja skaitītājs oom_kill pieaug, kodols pārtrauc worker. Tas var norādīt uz pārāk šauru robežu, kļūdainu lietojumprogrammu vai neparastiem pieprasījumiem. Pašreizējā situācijā ir vērts šādus notikumus salīdzināt ar piekļuves žurnāliem tajā pašā laikā.

Svarīgi

Atmiņas robeža ir pārejas pasākums un neaizstāj PHP atjauninājumu. Plānojiet atjauninājumu paralēli un pēc tam pārstartējiet visus PHP procesus. Visi pārejas pasākumi pārskatā atrodami sadaļā Tūlītēji pasākumi, ja vēl nevarat ielāpot.

Kas PHP iekšienē notiek atmiņas trūkuma gadījumā, izskaidro memory_limit un Out-of-Memory. Apache ar mod_php tā pati metode attiecas uz Apache pakalpojumu, tomēr PHP tad robežu dala ar pašu tīmekļa serveri.

Secinājums: neaizmirstiet otro pusi

memory_limit=-1 ir pieņemams pārejas pasākums tikai kopā ar ārēju robežu. systemd šo robežu ļauj iestatīt ar dažām rindiņām: MemoryHigh kā bremze, MemoryMax kā stingra augšējā robeža, MemorySwapMax pret izvairīšanos uz swap un apzināti izvēlēta OOMPolicy. Izmēriet iepriekš, novērojiet pēc tam un paturiet prātā, ka visi viena pakalpojuma pūli dala robežu.

Bet galvenais: plānojiet pasākuma beigas. Pēc atjauninājuma uz labotu PHP versiju atgriezieties pie stingra memory_limit. Cgroup robežu varat paturēt kā papildu nodrošinājumu pret izplūstošiem procesiem.

Kā PHP SOS palīdz konkrēti?

  • Mērīt: mēs uzskaitām jūsu PHP pakalpojumu atmiņas vajadzību un no tās atvasinām piemērotas robežas.
  • Īstenot: mēs iestatām drop-in ar MemoryHigh, MemoryMax un piemērotu OOMPolicy, nobloķējam memory_limit pūlā un kontrolēti pārstartējam pakalpojumus.
  • Novērot: OOM notikumus mēs iekļaujam uzraudzībā un vērtības pielāgojam.
  • Novest līdz ielāpam: ar PHP ielāpu pakalpojumu mēs ieviešam atjauninājumu un pēc tam sakārtoti atceļam pārejas vērtības.

Biežāk uzdotie jautājumi

Kāpēc memory_limit jābūt -1? Vai tas nav bīstami?

Vienam pašam tas būtu. php.net memory_limit=-1 kā pārejas pasākumu nepārprotami iesaka kopā ar atmiņas robežu caur konteineru vai cgroup. Ierobežojums tādējādi no PHP pārceļas uz operētājsistēmu. Bez šīs ārējās robežas vērtību nevajadzētu iestatīt.

Vai tā vietā varu izmantot ulimit vai LimitAS=?

Nē. php.net nepārprotami atsaka RLIMIT_AS un RLIMIT_DATA, un tieši šīs robežas iestata ulimit -v, LimitAS= un LimitDATA=. systemd dokumentācija turklāt norāda, ka LimitAS= ierobežo adrešu telpu, nevis atmiņas patēriņu.

Kas notiek, ja PHP-FPM sasniedz robežu?

Ar MemoryHigh kodols procesus bremzē un atgūst atmiņu. Ar MemoryMax pakalpojuma iekšienē kļūst aktīvs OOM-killer un pārtrauc procesu, parasti lielu worker. Vai pēc tam apstājas viss pakalpojums, nosaka OOMPolicy=.

Vai robeža darbojas katram pūlam atsevišķi?

Nē. Visi viena FPM pakalpojuma pūli dala vienu cgroup un tātad kopīgu robežu. Atsevišķām robežām katrai vietnei vajadzīgi atsevišķi FPM pakalpojumi vai konteineri.

Kā iestatīt robežu Docker vai Kubernetes?

Ar konteinera atmiņas ierobežojumu, piemēram, opciju --memory docker run vai resources.limits.memory Kubernetes. Abi arī izmanto cgroups. Princips paliek tas pats: robeža ārpus PHP, memory_limit PHP nobloķēts.

Avoti

Tūlītējs kontakts

Katra stunda ir svarīga. Rakstiet mums tūlīt.

Īsi aprakstiet sistēmu, PHP versiju un novēroto. Mēs sazināsimies, noskaidrosim situāciju un strādāsim attālināti, līdz incidents ir slēgts un dokumentēts.

Tūlītēja palīdzība