Kort sagt: För PHP2Shell-övergångsåtgärden memory_limit=-1 behöver PHP-FPM en minnesgräns utanför PHP. Under systemd sätter ni den med MemoryHigh= som broms och MemoryMax= som hård övre gräns i en tilläggsfil för FPM-tjänsten. ulimit, LimitAS= eller LimitDATA= är enligt php.net olämpliga för detta. Kontrollera dessutom OOMPolicy=, så att en enskild worker inte avslutar hela tjänsten.
En rekommendation som först väcker förvåning
Bland de övergångsåtgärder som php.net publicerade den 21 oktober 2026 om PHP2Shell finns en som fick många administratörer att haja till: memory_limit=-1, alltså ingen minnesgräns i själva PHP. I åratal gällde motsatsen som god praxis.
Ett samband med beskrivningen av sårbarheten ligger nära. Vid CVE-2026-109618 kan ett out-of-memory-tillstånd avbryta PHP medan förfrågningsdata bara är delvis initierade. Övergångsåtgärden flyttar minnesgränsen ut ur PHP och in i operativsystemet. php.net knyter den uttryckligen till två villkor: värdet måste vara spärrat mot överskrivning, och gränsen måste komma via container eller cgroup, inte via RLIMIT_AS eller RLIMIT_DATA.
Den som bara genomför den första hälften har en server utan någon minnesgräns alls. Det här inlägget visar den andra hälften. Även tillsammans förblir åtgärden en övergång: sårbarheten är åtgärdad först med PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 eller en bekräftad backport.
cgroups förklarade i två meningar
Control groups, förkortat cgroups, är en funktion i Linux-kärnan som grupperar processer och begränsar deras resurser. systemd skapar automatiskt en egen cgroup för varje tjänst, och alla tjänstens processer, vid FPM-tjänsten alltså master och samtliga workers, hör dit.
systemds minnesalternativ sätter värden i cgroup-v2-minneskontrollern. Om ert system använder cgroup v2 visar ett kort kommando:
# "cgroup2fs" betyder: cgroup v2 är aktivt
stat -fc %T /sys/fs/cgroup/
Aktuella versioner av de vanliga distributionerna använder cgroup v2 som standard. Visar kommandot något annat bör ni klargöra det före omställningen, eftersom systemd-dokumentationen beskriver de gamla v1-alternativen som föråldrade.
Sätta systemd MemoryMax för PHP-FPM
Gränsen läggs i en tilläggsfil, en så kallad drop-in. Då förblir paketets unit-fil oförändrad, och en uppdatering skriver inte över er inställning. Tjänstenamn skiljer sig mellan distributioner; exemplet använder Debian och Ubuntu med PHP 8.3.
# Öppnar en editor för drop-in: sudo systemctl edit php8.3-fpm
# Resultat: /etc/systemd/system/php8.3-fpm.service.d/override.conf
[Service]
MemoryAccounting=yes
MemoryHigh=1536M
MemoryMax=2G
MemorySwapMax=0
OOMPolicy=continue
Alternativen i överblick, enligt systemds dokumentation:
| Alternativ | Verkan | Anmärkning för PHP-FPM |
|---|---|---|
MemoryHigh= |
Strypgräns: processer bromsas kraftigt, minne tas tillbaka | den egentliga styrstorleken, något under MemoryMax |
MemoryMax= |
absolut gräns: går den inte att hålla blir OOM-killern aktiv i tjänsten | sista försvarslinjen |
MemorySwapMax= |
gräns för utlagring | 0 förhindrar att tjänsten i stället väjer ut i swap |
OOMPolicy= |
beteende efter en OOM-händelse: continue, stop eller kill |
continue låter övriga workers fortsätta köra |
LimitAS=, LimitDATA= |
rlimits per process | enligt php.net olämpliga för den här åtgärden |
OOMPolicy= förtjänar särskild uppmärksamhet. Vid stop eller kill avslutar systemd efter en OOM-händelse hela tjänsten, och alla webbplatser i den FPM-tjänsten slås ut. Vilket värde som gäller utan egen uppgift avgör systemstandarden DefaultOOMPolicy=. Kontrollera därför det verksamma värdet i stället för att anta det.
Starta därefter om tjänsten och spärra memory_limit i poolen:
sudo systemctl restart php8.3-fpm
systemctl show php8.3-fpm -p MemoryHigh -p MemoryMax -p MemorySwapMax -p OOMPolicy -p MemoryCurrent
; Poolfil, t.ex. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_value[memory_limit] = -1
Hur spärrandet fungerar beskriver php_admin_value: spärra inställningar mot överskrivning.
Hitta rätt värde
En för snäv gräns ger avbrott i normaldriften, en för generös skyddar knappt. Utgångspunkten är den faktiska förbrukningen.
- Observera det aktuella minnesbehovet för alla FPM-processer under några dagar, även vid belastningstoppar.
- Ta reda på det typiska behovet för en worker och multiplicera med
pm.max_childrenför alla poolerna i tjänsten. - Räkna in ett påslag för masterprocessen, OPcache och toppar.
- Sätt
MemoryHigh=strax över det uppmätta toppvärdet,MemoryMax=med lite marginal ovanför. - Justera värdena efter en vecka utifrån mätningarna och OOM-händelserna.
# Minne för alla PHP-FPM-processer i MB (Resident Set Size)
ps -C php-fpm8.3 -o rss= | awk '{s+=$1} END {printf "%.0f MB\n", s/1024}'
# Löpande översikt över alla cgroups sorterade efter minne
systemd-cgtop -m
Anteckna värdet för memory_limit före omställningen. Efter uppdateringen kan ni återgå till en fast gräns i PHP; hur ni väljer den förnuftigt står under Ställ in memory_limit rätt.
Övervaka: när gränsen slår till
En minnesgräns vars utlösande ingen märker är en tyst felkälla. Kärnan räknar OOM-händelser per cgroup, och systemd samt kärnloggen noterar dem.
# OOM-räknare för tjänstens cgroup (sökväg vid cgroup v2)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# Meddelanden från OOM-killern i kärnloggen
journalctl -k --since "-1 day" | grep -i "out of memory"
Stiger räknaren oom_kill avslutar kärnan workers. Det kan tyda på en för snäv gräns, på en felaktig applikation eller på ovanliga förfrågningar. I det aktuella läget lönar det sig att jämföra sådana händelser med åtkomstloggarna för samma tidpunkt.
Minnesgränsen är en övergångsåtgärd och ersätter inte PHP-uppdateringen. Planera uppdateringen parallellt och starta därefter om alla PHP-processer. Alla övergångsåtgärder i överblick finns under Akuta åtgärder när ni ännu inte kan patcha.
Vad som händer inuti PHP vid minnesbrist förklarar memory_limit och out-of-memory. Med Apache och mod_php gäller samma teknik för Apache-tjänsten, men då delar PHP gränsen med själva webbservern.
Slutsats: glöm inte den andra hälften
memory_limit=-1 är bara tillsammans med en yttre gräns en försvarbar övergångsåtgärd. systemd gör den gränsen möjlig med några få rader: MemoryHigh som broms, MemoryMax som hård övre gräns, MemorySwapMax mot att väja ut i swap och en medvetet vald OOMPolicy. Mät i förväg, observera efteråt och ha i åtanke att alla pooler i en tjänst delar gränsen.
Framför allt: planera åtgärdens slut. Efter uppdateringen till en rättad PHP-version återgår ni till en fast memory_limit. Cgroup-gränsen kan ni behålla som extra säkring mot skenande processer.
Hur hjälper PHP SOS konkret?
- Mäta: Vi kartlägger minnesbehovet för era PHP-tjänster och härleder lämpliga gränser.
- Genomföra: Vi sätter drop-ins med
MemoryHigh,MemoryMaxoch lämpligOOMPolicy, spärrarmemory_limiti poolen och startar om tjänsterna kontrollerat. - Observera: Vi bygger in OOM-händelser i övervakningen och justerar värdena.
- Leda till patchen: Via PHP-patchtjänsten installerar vi uppdateringen och tar därefter tillbaka övergångsvärdena ordnat.
Vanliga frågor
Varför ska memory_limit stå på -1? Är inte det farligt?
Ensamt vore det det. php.net rekommenderar memory_limit=-1 som övergångsåtgärd uttryckligen tillsammans med en minnesgräns via container eller cgroup. Begränsningen flyttar då ut ur PHP och in i operativsystemet. Utan den yttre gränsen bör ni inte sätta värdet.
Kan jag i stället använda ulimit eller LimitAS=?
Nej. php.net avråder uttryckligen från RLIMIT_AS och RLIMIT_DATA, och just dessa gränser sätter ulimit -v, LimitAS= och LimitDATA=. systemd-dokumentationen påpekar dessutom att LimitAS= begränsar adressutrymmet, inte minnesförbrukningen.
Vad händer när PHP-FPM når gränsen?
Vid MemoryHigh bromsar kärnan processerna och tar tillbaka minne. Vid MemoryMax blir OOM-killern aktiv inom tjänsten och avslutar en process, oftast en stor worker. Om hela tjänsten stoppas därefter styr OOMPolicy=.
Gäller gränsen för varje pool för sig?
Nej. Alla pooler i en FPM-tjänst delar en cgroup och därmed en gemensam gräns. Separata gränser per webbplats kräver separata FPM-tjänster eller containrar.
Hur sätter jag gränsen i Docker eller Kubernetes?
Via containerns minnesbegränsning, till exempel alternativet --memory vid docker run eller resources.limits.memory i Kubernetes. Båda använder också cgroups. Principen är densamma: gräns utanför PHP, memory_limit spärrat i PHP.