Stručně: Chyba PHP Out-of-Memory vzniká, když chce skript vyžádat více paměti, než povoluje nastavení memory_limit (standardně 128 MB na požadavek). PHP pak požadavek ukončí fatální chybou a uklidí. Právě tato cesta ukončení a úklidu byla chybná u CVE-2026-109618, jedné ze dvou zranitelností PHP2Shell. Od PHP 8.5.12, 8.4.27, 8.3.36 a 8.2.35 je memory_limit měkký limit, doplněný pevným limitem memory_hard_limit_percent.
Co znamená „Allowed memory size exhausted“?
Známé chybové hlášení zní v přeneseném smyslu: „Allowed memory size of 134217728 bytes exhausted (tried to allocate …)“. První číslo je nastavený limit v bajtech, zde 128 MB. Skript se tedy pokusil obsadit paměť nad tuto hranici, například při načítání velkého souboru, obrovském databázovém dotazu nebo kvůli nekonečné smyčce.
Pro rozhodovatele je důležité: limit platí pro každý proces PHP a každý požadavek, ne pro celý server. Chrání server před tím, aby jediný chybný skript spotřeboval veškerou paměť. Celkové zatížení vyplývá teprve z počtu současně běžících workerů. Při 20 workerech PHP-FPM s limitem 256 MB je to početně přes 5 GB.
Jak PHP počítá paměť
PHP spravuje paměť pro požadavky vlastním správcem paměti, Zend Memory Managerem. Ten získává větší bloky od operačního systému a vnitřně je rozděluje proměnným, polím a objektům. Proti této rezervované hodnotě PHP kontroluje memory_limit. Paměť, kterou si rozšíření vyžádá přímo u operačního systému, se podle příručky v tomto počítání neobjeví.
Hodnoty můžete zviditelnit v kódu:
<?php
echo memory_get_usage(), PHP_EOL; // paměť aktuálně obsazená skriptem
echo memory_get_usage(true), PHP_EOL; // rezervováno správcem paměti; proti tomu kontroluje memory_limit
$liste = range(1, 200000);
echo memory_get_peak_usage(), PHP_EOL; // dosavadní maximum
unset($liste);
memory_reset_peak_usage(); // vynulovat maximum, od PHP 8.2
Jak počítání referencí a garbage collection paměť opět uvolňují, vysvětluje stránka Správa paměti v PHP.
Co se při Out-of-Memory děje uvnitř
Zjednodušeně probíhá ukončení kvůli paměti v těchto krocích:
- Skript si vyžádá paměť, například pro nový prvek pole nebo při načítání dat požadavku.
- Správce paměti zkontroluje, zda požadavek překračuje limit.
- Pokud ano, PHP vyvolá fatální chybu a okamžitě opustí probíhající zpracování, i uprostřed napůl hotového úkolu.
- Registrované shutdown funkce ještě proběhnou; mnoho aplikací v nich chybu zaprotokoluje.
- Na konci PHP požadavek uklidí a uvolní jeho paměť. Worker je poté připraven na další požadavek.
Choulostivým místem je krok 5. Úklid si musí poradit s datovými strukturami, které byly při ukončení teprve napůl vytvořeny. Běžné ukončení kvůli paměti se podle bezpečnostní politiky projektu PHP nepovažuje za bezpečnostní zranitelnost, pokud limit požadavek odmítne a skončí jen aktuální požadavek. U CVE-2026-109618 byl právě tento předpoklad porušen: podle php.net mohl stav nedostatku paměti přerušit PHP ve chvíli, kdy byla interní data požadavku inicializována jen částečně, a úklid pak zacházel s neplatnými daty jako s platnými. Podrobnosti zneužití nezveřejňujeme; zařazení zranitelnosti najdete v CVE-2026-109618.
Nový měkký a pevný limit paměti
S bezpečnostními vydáními z 21. 10. 2026 projekt PHP chování změnil. Podle příručky je memory_limit od té doby měkký limit: požadavek, který jej překročí, smí být ve většině případů ještě dokončen, než následuje fatální chyba. Samostatný pevný limit určuje, jak daleko to smí zajít.
| Direktiva | Výchozí | Lze měnit | Význam |
|---|---|---|---|
memory_limit |
128M | INI_ALL | limit paměti na požadavek, -1 = bez limitu |
memory_hard_limit_percent |
nenastaveno | INI_ALL | pevný limit v procentech z memory_limit, nové od 8.5.12, 8.4.27, 8.3.36, 8.2.35 |
post_max_size |
8M | INI_PERDIR | maximální velikost dat POST |
upload_max_filesize |
2M | INI_PERDIR | maximální velikost nahraného souboru |
Příklad z příručky: při memory_limit=128M a memory_hard_limit_percent=150 leží pevný limit na 192 MB. Příručka tuto direktivu zároveň uvádí jako poznávací znamení: pokud ji verze PHP nezná, je CVE-2026-109618 a CVE-2026-109619 postižena. Rychlý test pro každou nainstalovanou verzi PHP:
php -r 'var_dump(ini_get("memory_hard_limit_percent"));'
# bool(false) znamená: direktiva neznámá
U balíčků z distribucí s backportem zůstává rozhodujícím důkazem changelog balíčku, jak popisuje článek Správná kontrola verze PHP a stavu záplat. Zkontrolujte navíc samostatně binárku PHP-FPM, protože může mít jinou verzi než příkazová řádka.
Limity paměti a PHP2Shell: čeho si nyní všímat
Přechodná opatření php.net k CVE-2026-109618 se téměř všechna točí kolem paměti a velikostí požadavků. Snižují riziko, ale aktualizaci nenahrazují:
; pool FPM, např. /etc/php/8.3/fpm/pool.d/www.conf
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M ; 32M, pokud jsou nutná nahrávání
php_admin_value[upload_max_filesize] = 6M ; držet pod post_max_size
Rozhodující jsou tři body. Zaprvé, žádná aplikace, žádný adresář ani jiný pool nesmí znovu nastavit konečný limit; php.net tuto kontrolu výslovně vyžaduje. Zadruhé, služba s memory_limit=-1 potřebuje limit zvenčí, přes kontejner nebo cgroup, podle php.net nejlépe tak, aby se ukončil jen dotčený worker. RLIMIT_AS a RLIMIT_DATA jsou podle php.net nevhodné. Zatřetí, php.net doporučuje limity na webovém serveru nebo proxy pro velikost požadavku, počet polí, části multipart, hlavičky a hloubku vnoření.
memory_limit=-1 bez limitu paměti zvenčí může při vysoké zátěži server úplně ochromit. Přechodná opatření nastavujte jen jako balík a jen do aktualizace.
Jak limit nastavit pomocí systemd, najdete v článku Limity paměti pomocí systemd a cgroups; trvalé řešení pro každodenní provoz popisuje článek Správné nastavení memory_limit.
Jak konkrétně pomáhá PHP SOS?
Prostřednictvím služby záplatování PHP postupujeme takto:
- Zkontrolovat stav: Zjistíme každou verzi PHP na serveru, odděleně pro CLI, FPM a modul Apache, a otestujeme, zda běží opravená verze.
- Uspořádat limity paměti: Zkontrolujeme, kde všude se
memory_limitnastavuje, a nastavíme limity poolů a systému tak, aby si odpovídaly. - Zajistit přechodné období: Kde je aktualizace ještě blokována, nastavíme opatření php.net kompletně a zablokovaná proti přepsání.
- Dodat doklad: Verze, stavy balíčků a konfiguraci před zásahem a po něm obdržíte písemně.
Časté dotazy
Co znamená hlášení „Allowed memory size exhausted“?
Skript chtěl vyžádat více paměti, než povoluje memory_limit. PHP pak probíhající požadavek ukončí fatální chybou. Ostatní požadavky i samotný server běží dál.
Platí memory_limit pro celý server?
Ne, limit platí pro každý proces PHP a požadavek. Pokud běží 20 workerů FPM s limitem 256 MB, mohou teoreticky společně obsadit přes 5 GB. Celkový limit je proto nutné nastavit přes velikost poolu, systemd nebo kontejner.
Co je memory_hard_limit_percent?
Nová direktiva, která přišla s PHP 8.5.12, 8.4.27, 8.3.36 a 8.2.35. Nastavuje pevný limit jako procento z memory_limit, který se nikdy nepřekročí. memory_limit je od té doby měkký limit.
Chrání memory_limit=-1 před PHP2Shell?
Jen částečně. php.net toto nastavení uvádí jako přechodné opatření proti CVE-2026-109618, výslovně bez úplné ochrany. Nutně potřebuje limit paměti zvenčí a aktualizace zůstává povinná.