Aktuel situation: PHP2Shell. To kritiske PHP-sårbarheder (CVSS 9,2) tillader skadelig kode uden login. Rettelser siden 21.10.2026.Hvad der skal gøres nu →

Sæt hukommelsesgrænser med systemd og cgroups

php.net anbefaler som PHP2Shell-overgangsforanstaltning memory_limit=-1 og en hukommelsesgrænse uden for PHP. Sådan sætter I grænsen med systemd og cgroups, uden at en enkelt worker trækker hele tjenesten med sig.

Status 5 min. læsetidHærdning og konfiguration

Kort sagt: Til PHP2Shell-overgangsforanstaltningen memory_limit=-1 har PHP-FPM brug for en hukommelsesgrænse uden for PHP. Under systemd sætter I den med MemoryHigh= som bremse og MemoryMax= som hård øvre grænse i en supplerende fil til FPM-tjenesten. ulimit, LimitAS= eller LimitDATA= er ifølge php.net uegnede hertil. Kontrollér desuden OOMPolicy=, så en enkelt worker ikke afslutter hele tjenesten.

En anbefaling, der først vækker undren

Blandt de overgangsforanstaltninger, som php.net offentliggjorde den 21. oktober 2026 til PHP2Shell, står én, der fik mange administratorer til at standse op: memory_limit=-1, altså ingen hukommelsesgrænse i selve PHP. I årevis gjaldt det modsatte som god praksis.

En sammenhæng med beskrivelsen af sårbarheden ligger lige for. Ved CVE-2026-109618 kan en out-of-memory-tilstand afbryde PHP, mens forespørgselsdata kun er delvist initialiseret. Overgangsforanstaltningen flytter hukommelsesgrænsen ud af PHP og ind i operativsystemet. php.net knytter den udtrykkeligt til to betingelser: Værdien skal være låst mod overskrivning, og grænsen skal komme via container eller cgroup, ikke via RLIMIT_AS eller RLIMIT_DATA.

Den, der kun gennemfører første halvdel, har en server uden nogen hukommelsesgrænse. Dette indlæg viser den anden halvdel. Også samlet forbliver foranstaltningen en overgang: Sårbarheden er først rettet med PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 eller en bekræftet backport.

cgroups forklaret i to sætninger

Control groups, kort cgroups, er en funktion i Linux-kernen, der grupperer processer og begrænser deres ressourcer. systemd opretter automatisk en egen cgroup til hver tjeneste, og alle tjenestens processer, ved FPM-tjenesten altså master og samtlige workers, hører med.

systemds hukommelsesindstillinger sætter værdier i cgroup-v2-hukommelsescontrolleren. Om jeres system bruger cgroup v2, viser en kort kommando:

# "cgroup2fs" betyder: cgroup v2 er aktiv
stat -fc %T /sys/fs/cgroup/

Aktuelle versioner af de gængse distributioner bruger cgroup v2 som standard. Viser kommandoen noget andet, så afklar det før omstillingen, for systemd-dokumentationen beskriver de gamle v1-indstillinger som forældede.

Sæt systemd MemoryMax til PHP-FPM

Grænsen kommer i en supplerende fil, et såkaldt drop-in. Så forbliver pakkens unit-fil uændret, og en opdatering overskriver ikke jeres indstilling. Tjenestenavne afviger fra distribution til distribution; eksemplet bruger Debian og Ubuntu med PHP 8.3.

# Åbner en editor til drop-in'et: 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

Indstillingerne i overblik, efter systemds dokumentation:

Indstilling Virkning Henvisning for PHP-FPM
MemoryHigh= drosselgrænse: processer bremses kraftigt, hukommelse hentes tilbage den egentlige styringsstørrelse, lidt under MemoryMax
MemoryMax= absolut grænse: kan den ikke holdes, bliver OOM-killeren aktiv i tjenesten sidste forsvarslinje
MemorySwapMax= grænse for udflytning 0 forhindrer, at tjenesten i stedet viger ud i swap
OOMPolicy= adfærd efter en OOM-hændelse: continue, stop eller kill continue lader de øvrige workers køre videre
LimitAS=, LimitDATA= rlimits pr. proces ifølge php.net uegnede til denne foranstaltning

OOMPolicy= fortjener særlig opmærksomhed. Ved stop eller kill afslutter systemd efter en OOM-hændelse hele tjenesten, og alle websites i denne FPM-tjeneste falder ud. Hvilken værdi der gælder uden egen angivelse, fastlægger systemstandarden DefaultOOMPolicy=. Kontrollér derfor den virksomme værdi i stedet for at antage den.

Genstart derefter tjenesten, og lås 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
; Pool-fil, f.eks. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_value[memory_limit] = -1

Hvordan låsningen fungerer, beskriver php_admin_value: Lås indstillinger mod overskrivning.

Find den rigtige værdi

En for snæver grænse giver afbrud i normal drift, en for rundhåndet beskytter næppe. Udgangspunktet er det faktiske forbrug.

  1. Observér det aktuelle hukommelsesbehov for alle FPM-processer over nogle dage, også ved belastningstoppe.
  2. Find en workers typiske behov, og gang det med pm.max_children for alle tjenestens pools.
  3. Indregn et tillæg til master-processen, OPcache og spidser.
  4. Sæt MemoryHigh= lige over den målte spidsværdi, MemoryMax= med lidt afstand derover.
  5. Finjustér værdierne efter en uge ud fra målingerne og OOM-hændelserne.
# Hukommelse for alle 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øbende oversigt over alle cgroups sorteret efter hukommelse
systemd-cgtop -m

Værdien for memory_limit før omstillingen bør I notere. Efter opdateringen kan I vende tilbage til en fast grænse i PHP; hvordan I vælger den fornuftigt, står under Sæt memory_limit rigtigt.

Overvåg: Hvornår grænsen virker

En hukommelsesgrænse, hvis udløsning ingen bemærker, er en stille fejlkilde. Kernen tæller OOM-hændelser pr. cgroup, og systemd samt kernelogget noterer dem.

# OOM-tæller for tjenestens cgroup (sti ved cgroup v2)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# Meldinger fra OOM-killeren i kernelogget
journalctl -k --since "-1 day" | grep -i "out of memory"

Stiger tælleren oom_kill, afslutter kernen workers. Det kan pege på en for snæver grænse, på en fejlbehæftet applikation eller på usædvanlige forespørgsler. I den aktuelle situation er det umagen værd at afstemme sådanne hændelser med adgangsloggene fra samme tidspunkt.

Vigtigt

Hukommelsesgrænsen er en overgangsforanstaltning og erstatter ikke PHP-opdateringen. Planlæg opdateringen parallelt, og genstart derefter alle PHP-processer. Alle overgangsforanstaltninger i overblik står under Akutforanstaltninger, hvis I endnu ikke kan patche.

Hvad der sker inde i PHP ved hukommelsesmangel, forklarer memory_limit og out-of-memory. Ved Apache med mod_php gælder den samme teknik for Apache-tjenesten, dog deler PHP så grænsen med selve webserveren.

Konklusion: Glem ikke den anden halvdel

memory_limit=-1 er kun sammen med en ydre grænse en forsvarlig overgangsforanstaltning. systemd gør denne grænse mulig med få linjer: MemoryHigh som bremse, MemoryMax som hård øvre grænse, MemorySwapMax mod udvigning til swap og en bevidst valgt OOMPolicy. Mål først, observér derefter, og hav in mente, at alle pools i en tjeneste deler grænsen.

Frem for alt: Planlæg foranstaltningens ophør. Efter opdateringen til en rettet PHP-version vender I tilbage til en fast memory_limit. Cgroup-grænsen kan I beholde som ekstra sikring mod løbske processer.

Hvordan hjælper PHP SOS konkret?

  • Mål: Vi registrerer hukommelsesbehovet i jeres PHP-tjenester og udleder passende grænser deraf.
  • Gennemfør: Vi sætter drop-ins med MemoryHigh, MemoryMax og passende OOMPolicy, låser memory_limit i poolen og genstarter tjenesterne kontrolleret.
  • Observér: Vi indbinder OOM-hændelser i overvågningen og finjusterer værdierne.
  • Før frem til patchen: Via PHP-patch-servicen indspiller vi opdateringen og tager derefter overgangsværdierne tilbage på ordnet vis.

Ofte stillede spørgsmål

Hvorfor skal memory_limit stå på -1? Er det ikke farligt?

Alene ville det være det. php.net anbefaler memory_limit=-1 som overgangsforanstaltning udtrykkeligt sammen med en hukommelsesgrænse via container eller cgroup. Begrænsningen flytter dermed ud af PHP og ind i operativsystemet. Uden denne ydre grænse bør I ikke sætte værdien.

Kan jeg i stedet bruge ulimit eller LimitAS=?

Nej. php.net fraråder udtrykkeligt RLIMIT_AS og RLIMIT_DATA, og netop disse grænser sætter ulimit -v, LimitAS= og LimitDATA=. systemd-dokumentationen peger desuden på, at LimitAS= begrænser adresserummet, ikke hukommelsesforbruget.

Hvad sker der, når PHP-FPM når grænsen?

Ved MemoryHigh bremser kernen processerne og henter hukommelse tilbage. Ved MemoryMax bliver OOM-killeren aktiv inde i tjenesten og afslutter en proces, som regel en stor worker. Om hele tjenesten derefter standser, afgør OOMPolicy=.

Gælder grænsen for hver pool for sig?

Nej. Alle pools i en FPM-tjeneste deler en cgroup og dermed en fælles grænse. Adskilte grænser pr. website kræver adskilte FPM-tjenester eller containere.

Hvordan sætter jeg grænsen i Docker eller Kubernetes?

Via containerens hukommelsesbegrænsning, f.eks. tilvalget --memory ved docker run eller resources.limits.memory i Kubernetes. Begge bruger ligeledes cgroups. Grundsætningen er den samme: grænse uden for PHP, memory_limit i PHP låst.

Kilder

Akut kontakt

Hver time tæller. Skriv til os nu.

Beskriv kort system, PHP-version og observation. Vi vender tilbage, afklarer situationen og arbejder på afstand, indtil hændelsen er afsluttet og dokumenteret.

Akut hjælp