# memory_limit e Out-of-Memory: o que acontece por dentro

> PHP Out of Memory explicado: como o memory_limit conta, o que acontece internamente na interrupção e o que isso significa para a CVE-2026-109618 e os seus servidores.

Fonte: https://phpsos.de/pt/php/out-of-memory/  
Atualizado em: 25/10/2026  
Prestador: PHP SOS, Assistência de emergência PHP e segurança de servidores. Ajuda imediata por WhatsApp: https://wa.me/491736494914?text=Ol%C3%A1%2C%20PHP%20SOS%2C%20precisamos%20de%20ajuda%20com%20o%20nosso%20servidor%20PHP.%20Empresa%3A%20%E2%80%A6%20Site%3A%20%E2%80%A6

**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
<?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](https://phpsos.de/pt/php/speicherverwaltung/).

## O que acontece por dentro num out-of-memory

De forma simplificada, uma interrupção por falta de memória decorre nestes passos:

1. O script pede memória, por exemplo para um novo elemento de array ou ao ler dados do pedido.
2. O gestor de memória verifica se o pedido excede o limite.
3. 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.
4. As funções de shutdown registadas ainda correm; é com elas que muitas aplicações registam o erro.
5. 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](https://phpsos.de/pt/php2shell/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:

```bash
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](https://phpsos.de/pt/php2shell/php-version-pruefen/). 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:

```ini
; 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.

> **Importante:**
> 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](https://phpsos.de/pt/blog/speichergrenzen-systemd-cgroups/); a solução duradoura para o dia a dia é descrita em [Definir corretamente o memory_limit](https://phpsos.de/pt/blog/memory-limit-richtig-setzen/).

## Como ajuda concretamente a PHP SOS?

Através do [serviço de patches PHP](https://phpsos.de/pt/leistungen/php-patch-service/), 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.

## Fontes

- [Manual do PHP: definições do núcleo (memory_limit, memory_hard_limit_percent)](https://www.php.net/manual/en/ini.core.php)
- [Manual do PHP: memory_get_usage()](https://www.php.net/manual/en/function.memory-get-usage.php)
- [php.net: avisos de segurança de 21/10/2026](https://www.php.net/)
