Em resumo: Um erro de falta de memória (out of memory) do PHP surge quando um script quer pedir mais memória do que a definição memory_limit permite (predefinição: 128 MB por pedido). O PHP interrompe então o pedido com um erro fatal e limpa. Foi precisamente este caminho de interrupção e de limpeza que estava com falhas na CVE-2026-109618, uma das duas vulnerabilidades do PHP2Shell. Desde o PHP 8.5.12, 8.4.27, 8.3.36 e 8.2.35, o memory_limit é um limite flexível, complementado pelo limite rígido memory_hard_limit_percent.
O que significa «Allowed memory size exhausted»?
A conhecida mensagem de erro diz, em substância: «Allowed memory size of 134217728 bytes exhausted (tried to allocate …)». O primeiro número é o limite definido em bytes, aqui 128 MB. Um script tentou, portanto, ocupar memória para além deste limite, por exemplo ao carregar um ficheiro grande, numa consulta enorme à base de dados ou por causa de um ciclo infinito.
Importante para quem decide: o limite aplica-se por processo PHP e por pedido, não ao servidor inteiro. Protege o servidor de que um único script defeituoso consuma toda a memória. A carga total só resulta do número de workers a correr em simultâneo. Com 20 workers PHP-FPM, cada um com um limite de 256 MB, são, em termos de cálculo, mais de 5 GB.
Como o PHP conta a memória
O PHP gere a memória dos pedidos através de um gestor de memória próprio, o Zend Memory Manager. Obtém blocos maiores ao sistema operativo e distribui-os internamente por variáveis, arrays e objetos. É contra este valor reservado que o PHP verifica o memory_limit. A memória que uma extensão pede diretamente ao sistema operativo não aparece, segundo o manual, nesta contagem.
Pode tornar os valores visíveis no código:
<?php
echo memory_get_usage(), PHP_EOL; // memória atualmente ocupada pelo script
echo memory_get_usage(true), PHP_EOL; // reservada pelo gestor de memória; é contra ela que o memory_limit verifica
$liste = range(1, 200000);
echo memory_get_peak_usage(), PHP_EOL; // valor máximo até agora
unset($liste);
memory_reset_peak_usage(); // repor o valor máximo, a partir do PHP 8.2
Como a contagem de referências e a garbage collection voltam a libertar a memória explica-o a página Gestão de memória em PHP.
O que acontece por dentro num out-of-memory
De forma simplificada, uma interrupção por falta de memória decorre nestes passos:
- O script pede memória, por exemplo para um novo elemento de array ou ao ler dados do pedido.
- O gestor de memória verifica se o pedido excede o limite.
- Se for o caso, o PHP desencadeia um erro fatal e abandona de imediato o processamento em curso, mesmo a meio de uma tarefa meio concluída.
- As funções de shutdown registadas ainda correm; é com elas que muitas aplicações registam o erro.
- No fim, o PHP limpa o pedido e liberta a sua memória. O worker fica depois pronto para o pedido seguinte.
O ponto delicado é o passo 5. A limpeza tem de lidar com estruturas de dados que, na interrupção, estavam apenas meio criadas. Uma interrupção de memória normal não é, segundo a política de segurança do projeto PHP, uma vulnerabilidade, desde que o limite rejeite o pedido e termine apenas o pedido atual. Na CVE-2026-109618, foi precisamente este pressuposto que foi violado: segundo o php.net, o estado de falta de memória podia interromper o PHP enquanto dados internos do pedido estavam apenas parcialmente inicializados, e a limpeza tratava então dados inválidos como válidos. Não publicamos detalhes da exploração; o enquadramento da vulnerabilidade está em CVE-2026-109618.
O novo limite de memória flexível e rígido
Com as versões de segurança de 21/10/2026, o projeto PHP alterou o comportamento. Segundo o manual, o memory_limit é, desde então, um limite flexível: um pedido que o exceda pode, na maioria dos casos, ainda ser concluído antes de se seguir o erro fatal. Um limite rígido separado delimita até onde isso pode ir.
| Diretiva | Predefinição | Alterável | Significado |
|---|---|---|---|
memory_limit |
128M | INI_ALL | Limite de memória por pedido, -1 = sem limite |
memory_hard_limit_percent |
não definido | INI_ALL | Limite rígido em percentagem do memory_limit, novo desde 8.5.12, 8.4.27, 8.3.36, 8.2.35 |
post_max_size |
8M | INI_PERDIR | Tamanho máximo dos dados POST |
upload_max_filesize |
2M | INI_PERDIR | Tamanho máximo de um ficheiro enviado |
Um exemplo do manual: com memory_limit=128M e memory_hard_limit_percent=150, o limite rígido situa-se nos 192 MB. O manual indica a diretiva também como sinal de reconhecimento: se uma versão do PHP não a conhece, está afetada pela CVE-2026-109618 e pela CVE-2026-109619. Um teste rápido para cada versão do PHP instalada:
php -r 'var_dump(ini_get("memory_hard_limit_percent"));'
# bool(false) significa: diretiva desconhecida
No caso de pacotes de distribuições com backport, o changelog do pacote continua a ser a prova determinante, como descrito em Verificar corretamente a versão do PHP e o nível de patches. Verifique também em separado o binário do PHP-FPM, pois pode ter uma versão diferente da linha de comandos.
Limites de memória e PHP2Shell: o que os administradores devem ter em conta agora
As medidas provisórias do php.net para a CVE-2026-109618 giram quase todas em torno da memória e dos tamanhos de pedido. Reduzem o risco, mas não substituem a atualização:
; pool FPM, p. ex. /etc/php/8.3/fpm/pool.d/www.conf
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M ; 32M, se forem necessários uploads
php_admin_value[upload_max_filesize] = 6M ; manter abaixo de post_max_size
Três pontos são decisivos. Primeiro, nenhuma aplicação, nenhum diretório e nenhum outro pool pode voltar a definir um limite finito; o php.net exige expressamente este controlo. Segundo, um serviço com memory_limit=-1 precisa de um limite externo, através de contentor ou cgroup, segundo o php.net de preferência de modo a terminar apenas o worker afetado. RLIMIT_AS e RLIMIT_DATA não são adequados, segundo o php.net. Terceiro, o php.net recomenda limites no servidor web ou no proxy para o tamanho do pedido, o número de campos, as partes multipart, os cabeçalhos e a profundidade de aninhamento.
O memory_limit=-1 sem limite de memória externo pode paralisar completamente um servidor com carga elevada. Aplique as medidas provisórias apenas em conjunto e apenas até à atualização.
Como definir o limite com o systemd está em Limites de memória com systemd e cgroups; a solução duradoura para o dia a dia é descrita em Definir corretamente o memory_limit.
Como ajuda concretamente a PHP SOS?
Através do serviço de patches PHP, procedemos assim:
- Verificar o parque instalado: registamos cada versão do PHP no servidor, separada por CLI, FPM e módulo Apache, e testamos se a versão corrigida está a correr.
- Ordenar os limites de memória: verificamos onde o
memory_limité definido e configuramos os limites de pool e de sistema de modo a que sejam coerentes. - Proteger a transição: onde a atualização ainda está bloqueada, aplicamos as medidas do php.net por completo e bloqueadas contra sobreposição.
- Entregar a prova: recebe por escrito as versões, os estados dos pacotes e a configuração antes e depois da intervenção.
Perguntas frequentes
O que significa a mensagem «Allowed memory size exhausted»?
Um script quis pedir mais memória do que o memory_limit permite. O PHP interrompe então o pedido em curso com um erro fatal. Os outros pedidos e o próprio servidor continuam a funcionar.
O memory_limit aplica-se ao servidor inteiro?
Não, o limite aplica-se por processo PHP e por pedido. Se 20 workers FPM correrem, cada um com um limite de 256 MB, podem em teoria ocupar em conjunto mais de 5 GB. O limite global tem por isso de ser definido através do tamanho do pool, do systemd ou de contentores.
O que é o memory_hard_limit_percent?
É uma nova diretiva, introduzida com o PHP 8.5.12, 8.4.27, 8.3.36 e 8.2.35. Define um limite rígido, como percentagem do memory_limit, que nunca é excedido. O próprio memory_limit é, desde então, um limite flexível.
O memory_limit=-1 protege do PHP2Shell?
Só em parte. O php.net indica a definição como medida provisória contra a CVE-2026-109618, expressamente sem proteção completa. Exige obrigatoriamente um limite de memória externo, e a atualização continua a ser obrigatória.