Situazione attuale: PHP2Shell. Due vulnerabilità critiche di PHP (CVSS 9,2) consentono codice dannoso senza autenticazione. Patch dal 21/10/2026.Che cosa fare ora →

Impostare limiti di memoria con systemd e cgroup

Come misura transitoria per PHP2Shell php.net raccomanda memory_limit=-1 e un limite di memoria fuori da PHP. Come impostare questo limite con systemd e cgroup, senza che un singolo worker trascini con sé l'intero servizio.

Stato 6 min di letturaHardening e configurazione

In breve: per la misura transitoria di PHP2Shell memory_limit=-1, PHP-FPM ha bisogno di un limite di memoria fuori da PHP. Sotto systemd lo imposta con MemoryHigh= come freno e MemoryMax= come tetto rigido in un file integrativo per il servizio FPM. ulimit, LimitAS= o LimitDATA= sono inadatti allo scopo secondo php.net. Verifichi inoltre OOMPolicy=, affinché un singolo worker non termini l'intero servizio.

Una raccomandazione che all'inizio sorprende

Tra le misure transitorie che php.net ha pubblicato il 21 ottobre 2026 su PHP2Shell ce n'è una che ha fatto esitare molti amministratori: memory_limit=-1, cioè nessun limite di memoria in PHP stesso. Per anni valeva il contrario come buona pratica.

Un nesso con la descrizione della vulnerabilità è evidente. In CVE-2026-109618 uno stato di out-of-memory può interrompere PHP mentre i dati della richiesta sono inizializzati solo in parte. La misura transitoria sposta il limite di memoria da PHP al sistema operativo. php.net la collega espressamente a due condizioni: il valore deve essere bloccato contro la sovrascrittura e il limite deve venire da container o cgroup, non da RLIMIT_AS o RLIMIT_DATA.

Chi attua solo la prima metà ha un server senza alcun limite di memoria. Questo articolo mostra la seconda metà. Anche insieme, la misura resta un passaggio transitorio: la vulnerabilità è corretta solo con PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 o con un backport confermato.

Le cgroup spiegate in due frasi

I control group, in breve cgroup, sono una funzione del kernel Linux che raggruppa i processi e ne limita le risorse. systemd crea automaticamente per ogni servizio una cgroup propria, e ne fanno parte tutti i processi del servizio, nel caso del servizio FPM il master e tutti i worker.

Le opzioni di memoria di systemd impostano valori del controller di memoria cgroup v2. Se il Suo sistema usi cgroup v2 lo mostra un breve comando:

# "cgroup2fs" significa: cgroup v2 è attiva
stat -fc %T /sys/fs/cgroup/

Le versioni attuali delle distribuzioni più diffuse usano cgroup v2 per impostazione predefinita. Se il comando mostra altro, lo chiarisca prima del passaggio, perché la documentazione di systemd descrive le vecchie opzioni v1 come obsolete.

Impostare systemd MemoryMax per PHP-FPM

Il limite va in un file integrativo, il cosiddetto drop-in. Così il file unit del pacchetto resta invariato e un aggiornamento non sovrascrive la Sua impostazione. I nomi dei servizi variano secondo la distribuzione; l'esempio usa Debian e Ubuntu con PHP 8.3.

# Apre un editor per il drop-in: sudo systemctl edit php8.3-fpm
# Risultato: /etc/systemd/system/php8.3-fpm.service.d/override.conf
[Service]
MemoryAccounting=yes
MemoryHigh=1536M
MemoryMax=2G
MemorySwapMax=0
OOMPolicy=continue

Le opzioni in sintesi, secondo la documentazione di systemd:

Opzione Effetto Nota per PHP-FPM
MemoryHigh= soglia di rallentamento: i processi vengono fortemente frenati, la memoria viene recuperata la vera grandezza di controllo, un po' sotto MemoryMax
MemoryMax= limite assoluto: se non è possibile rispettarlo, si attiva l'OOM killer nel servizio ultima linea di difesa
MemorySwapMax= limite per lo swapping 0 impedisce che il servizio ripieghi sullo swap
OOMPolicy= comportamento dopo un evento OOM: continue, stop o kill continue lascia proseguire gli altri worker
LimitAS=, LimitDATA= rlimit per processo inadatti a questa misura secondo php.net

OOMPolicy= merita particolare attenzione. Con stop o kill, dopo un evento OOM systemd termina l'intero servizio e tutti i siti di quel servizio FPM vanno fuori uso. Quale valore valga senza indicazione propria lo stabilisce l'impostazione di sistema DefaultOOMPolicy=. Verifichi quindi il valore effettivo invece di darlo per scontato.

Poi riavvii il servizio e blocchi memory_limit nel pool:

sudo systemctl restart php8.3-fpm
systemctl show php8.3-fpm -p MemoryHigh -p MemoryMax -p MemorySwapMax -p OOMPolicy -p MemoryCurrent
; File del pool, ad es. /etc/php/8.3/fpm/pool.d/seite1.conf
php_admin_value[memory_limit] = -1

Come funziona il blocco lo descrive php_admin_value: bloccare le impostazioni contro la sovrascrittura.

Trovare il valore giusto

Un limite troppo stretto genera interruzioni nell'esercizio normale, uno troppo generoso protegge poco. Il punto di partenza è il consumo effettivo.

  1. Osservare per alcuni giorni il fabbisogno di memoria di tutti i processi FPM, anche nei picchi di carico.
  2. Stabilire il fabbisogno tipico di un worker e moltiplicarlo per pm.max_children di tutti i pool del servizio.
  3. Prevedere un margine per il processo master, OPcache e i picchi.
  4. Impostare MemoryHigh= appena sopra il picco misurato, MemoryMax= sopra con un certo scarto.
  5. Dopo una settimana, riaggiustare i valori in base alle misurazioni e agli eventi OOM.
# Memoria di tutti i processi PHP-FPM in MB (Resident Set Size)
ps -C php-fpm8.3 -o rss= | awk '{s+=$1} END {printf "%.0f MB\n", s/1024}'
# Panoramica in tempo reale di tutte le cgroup ordinate per memoria
systemd-cgtop -m

Il valore di memory_limit prima del passaggio dovrebbe annotarlo. Dopo l'aggiornamento può tornare a un limite fisso in PHP; come sceglierlo in modo sensato è spiegato in Impostare correttamente memory_limit.

Monitorare: quando il limite interviene

Un limite di memoria il cui intervento nessuno nota è una fonte silenziosa di errori. Il kernel conta gli eventi OOM per cgroup, e systemd e il registro del kernel li annotano.

# Contatore OOM della cgroup del servizio (percorso con cgroup v2)
cat /sys/fs/cgroup/system.slice/php8.3-fpm.service/memory.events
# Messaggi dell'OOM killer nel registro del kernel
journalctl -k --since "-1 day" | grep -i "out of memory"

Se il contatore oom_kill aumenta, il kernel termina dei worker. Può indicare un limite troppo stretto, un'applicazione difettosa o richieste insolite. Nella situazione attuale vale la pena di confrontare questi eventi con i log di accesso dello stesso momento.

Importante

Il limite di memoria è una misura transitoria e non sostituisce l'aggiornamento di PHP. Pianifichi l'aggiornamento in parallelo e riavvii poi tutti i processi PHP. Il quadro di tutte le misure transitorie è in Misure immediate se non può ancora applicare la patch.

Che cosa accade all'interno di PHP in caso di memoria insufficiente lo spiega memory_limit e out-of-memory. Con Apache e mod_php vale la stessa tecnica per il servizio Apache, ma PHP condivide allora il limite con il server web stesso.

Conclusione: non dimenticare la seconda metà

memory_limit=-1 è una misura transitoria accettabile solo insieme a un limite esterno. systemd rende possibile questo limite con poche righe: MemoryHigh come freno, MemoryMax come tetto rigido, MemorySwapMax contro il ripiego sullo swap e una OOMPolicy scelta consapevolmente. Misuri prima, osservi dopo e tenga presente che tutti i pool di un servizio condividono il limite.

Soprattutto però: pianifichi la fine della misura. Dopo l'aggiornamento a una versione di PHP corretta torni a un memory_limit fisso. Il limite cgroup può mantenerlo come ulteriore protezione contro processi fuori controllo.

Come aiuta PHP SOS in concreto?

  • Misurare: rileviamo il fabbisogno di memoria dei Suoi servizi PHP e ne ricaviamo limiti adatti.
  • Attuare: impostiamo drop-in con MemoryHigh, MemoryMax e una OOMPolicy adeguata, blocchiamo memory_limit nel pool e riavviamo in modo controllato i servizi.
  • Osservare: integriamo gli eventi OOM nel monitoraggio e riaggiustiamo i valori.
  • Portare alla patch: tramite il servizio di patch PHP applichiamo l'aggiornamento e ritiriamo poi in modo ordinato i valori transitori.

Domande frequenti

Perché memory_limit deve essere -1? Non è pericoloso?

Da solo lo sarebbe. php.net raccomanda memory_limit=-1 come misura transitoria espressamente insieme a un limite di memoria tramite container o cgroup. Il limite passa così da PHP al sistema operativo. Senza questo limite esterno non dovrebbe impostare il valore.

Posso usare invece ulimit o LimitAS=?

No. php.net sconsiglia espressamente RLIMIT_AS e RLIMIT_DATA, ed è proprio questi limiti che impostano ulimit -v, LimitAS= e LimitDATA=. La documentazione di systemd fa inoltre notare che LimitAS= limita lo spazio di indirizzamento, non il consumo di memoria.

Che cosa succede quando PHP-FPM raggiunge il limite?

Con MemoryHigh il kernel rallenta i processi e recupera memoria. Con MemoryMax si attiva all'interno del servizio l'OOM killer, che termina un processo, di solito un worker grande. Se poi si fermi l'intero servizio lo regola OOMPolicy=.

Il limite vale per ogni pool singolarmente?

No. Tutti i pool di un servizio FPM condividono una cgroup e quindi un limite comune. Limiti separati per ogni sito richiedono servizi FPM o container separati.

Come imposto il limite in Docker o Kubernetes?

Tramite il limite di memoria del container, ad esempio l'opzione --memory in docker run o resources.limits.memory in Kubernetes. Entrambi usano a loro volta le cgroup. Il principio resta lo stesso: limite fuori da PHP, memory_limit in PHP bloccato.

Fonti

Contatto immediato

Ogni ora conta. Ci scriva adesso.

Descriva brevemente sistema, versione di PHP e quanto osservato. Ci faremo sentire, chiariremo la situazione e lavoreremo da remoto fino alla chiusura e alla documentazione dell'incidente.

Aiuto immediato