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.
- Osservare per alcuni giorni il fabbisogno di memoria di tutti i processi FPM, anche nei picchi di carico.
- Stabilire il fabbisogno tipico di un worker e moltiplicarlo per
pm.max_childrendi tutti i pool del servizio. - Prevedere un margine per il processo master, OPcache e i picchi.
- Impostare
MemoryHigh=appena sopra il picco misurato,MemoryMax=sopra con un certo scarto. - 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.
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,MemoryMaxe unaOOMPolicyadeguata, blocchiamomemory_limitnel 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.