Aktualna sytuacja: PHP2Shell. Dwie krytyczne luki w PHP (CVSS 9,2) umożliwiają wykonanie złośliwego kodu bez logowania. Poprawki od 21.10.2026.Co należy teraz zrobić →

memory_limit i Out-of-Memory: co dzieje się w środku

Co się dzieje, gdy skrypt PHP uderza w memory_limit? Mechanika, nowy twardy limit pamięci od październikowych poprawek i związek z CVE-2026-109618.

Stan 5 min czytaniaWiedza o PHP

Krótko: Błąd PHP Out-of-Memory powstaje, gdy skrypt chce zająć więcej pamięci, niż pozwala ustawienie memory_limit (domyślnie 128 MB na żądanie). PHP przerywa wtedy żądanie błędem krytycznym i sprząta. Właśnie ta droga przerwania i sprzątania była błędna w CVE-2026-109618, jednej z dwóch luk PHP2Shell. Od PHP 8.5.12, 8.4.27, 8.3.36 i 8.2.35 memory_limit jest limitem miękkim, uzupełnionym o limit twardy memory_hard_limit_percent.

Co oznacza „Allowed memory size exhausted”?

Znany komunikat błędu brzmi w przybliżeniu: „Allowed memory size of 134217728 bytes exhausted (tried to allocate …)”. Pierwsza liczba to ustawiony limit w bajtach, tu 128 MB. Skrypt próbował więc zająć pamięć ponad tę granicę, na przykład przy wczytywaniu dużego pliku, ogromnego zapytania do bazy danych albo w pętli nieskończonej.

Dla osób podejmujących decyzje ważne jest to, że limit obowiązuje dla każdego procesu PHP i każdego żądania, a nie dla całego serwera. Chroni serwer przed tym, by pojedynczy błędny skrypt zużył całą pamięć. Całkowite obciążenie wynika dopiero z liczby jednocześnie działających workerów. Przy 20 workerach PHP-FPM z limitem 256 MB każdy daje to rachunkowo ponad 5 GB.

Jak PHP liczy pamięć

PHP zarządza pamięcią dla żądań przez własnego menedżera pamięci, Zend Memory Manager. Pobiera on większe bloki od systemu operacyjnego i rozdziela je wewnętrznie na zmienne, tablice i obiekty. Względem tej zarezerwowanej wartości PHP sprawdza memory_limit. Pamięć, którą rozszerzenie zamawia bezpośrednio od systemu operacyjnego, według podręcznika w tym liczeniu nie występuje.

Wartości można uwidocznić w kodzie:

<?php
echo memory_get_usage(), PHP_EOL;       // pamięć aktualnie zajęta przez skrypt
echo memory_get_usage(true), PHP_EOL;   // zarezerwowana przez menedżera pamięci; względem tego sprawdza memory_limit
$liste = range(1, 200000);
echo memory_get_peak_usage(), PHP_EOL;  // dotychczasowa wartość szczytowa
unset($liste);
memory_reset_peak_usage();              // reset wartości szczytowej, od PHP 8.2

Jak zliczanie referencji i garbage collection ponownie zwalniają pamięć, wyjaśnia strona Zarządzanie pamięcią w PHP.

Co dzieje się w środku przy Out-of-Memory

W uproszczeniu przerwanie z powodu pamięci przebiega w tych krokach:

  1. Skrypt zamawia pamięć, na przykład na nowy element tablicy lub przy wczytywaniu danych żądania.
  2. Menedżer pamięci sprawdza, czy zamówienie przekracza limit.
  3. Jeśli tak, PHP wywołuje błąd krytyczny i natychmiast opuszcza bieżące przetwarzanie, także w połowie niedokończonego zadania.
  4. Zarejestrowane funkcje shutdown jeszcze się wykonują; wiele aplikacji zapisuje w ten sposób błąd w logu.
  5. Na końcu PHP sprząta żądanie i zwalnia jego pamięć. Worker jest potem gotowy na następne żądanie.

Newralgicznym punktem jest krok 5. Sprzątanie musi radzić sobie ze strukturami danych, które w chwili przerwania były założone dopiero w połowie. Zwykłe przerwanie z powodu pamięci nie jest według polityki bezpieczeństwa projektu PHP luką bezpieczeństwa, dopóki limit odrzuca zamówienie i kończy się tylko bieżące żądanie. W CVE-2026-109618 to założenie zostało naruszone: według php.net stan Out-of-Memory mógł przerwać PHP, gdy wewnętrzne dane żądania były zainicjalizowane tylko częściowo, a sprzątanie traktowało wtedy nieprawidłowe dane jako prawidłowe. Szczegółów wykorzystania nie publikujemy; ocena luki znajduje się pod hasłem CVE-2026-109618.

Nowy limit miękki i twardy

Wraz z wydaniami bezpieczeństwa z 21.10.2026 projekt PHP zmienił zachowanie. Według podręcznika memory_limit jest od tego czasu limitem miękkim: zamówienie, które go przekracza, może w większości przypadków jeszcze zostać zakończone, zanim nastąpi błąd krytyczny. Osobny limit twardy ogranicza, jak daleko może to zajść.

Dyrektywa Domyślnie Zmienialna Znaczenie
memory_limit 128M INI_ALL limit pamięci na żądanie, -1 = brak limitu
memory_hard_limit_percent nieustawiona INI_ALL limit twardy w procentach memory_limit, nowość od 8.5.12, 8.4.27, 8.3.36, 8.2.35
post_max_size 8M INI_PERDIR maksymalny rozmiar danych POST
upload_max_filesize 2M INI_PERDIR maksymalny rozmiar przesłanego pliku

Przykład z podręcznika: przy memory_limit=128M i memory_hard_limit_percent=150 limit twardy wynosi 192 MB. Podręcznik podaje tę dyrektywę jednocześnie jako cechę rozpoznawczą: jeśli wersja PHP jej nie zna, jest dotknięta przez CVE-2026-109618 i CVE-2026-109619. Szybki test dla każdej zainstalowanej wersji PHP:

php -r 'var_dump(ini_get("memory_hard_limit_percent"));'
# bool(false) oznacza: dyrektywa nieznana

W przypadku pakietów z dystrybucji z backportem rozstrzygającym dowodem pozostaje changelog pakietu, jak opisano w artykule Prawidłowe sprawdzanie wersji PHP i stanu poprawek. Proszę ponadto osobno sprawdzić binarkę PHP-FPM, ponieważ może mieć inną wersję niż wiersz poleceń.

Limity pamięci a PHP2Shell: na co administratorzy zwracają teraz uwagę

Środki przejściowe php.net dotyczące CVE-2026-109618 niemal wszystkie dotyczą pamięci i rozmiarów żądań. Zmniejszają ryzyko, ale nie zastępują aktualizacji:

; pula FPM, np. /etc/php/8.3/fpm/pool.d/www.conf
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M        ; 32M, jeśli potrzebne są przesyłane pliki
php_admin_value[upload_max_filesize] = 6M  ; utrzymywać poniżej post_max_size

Decydujące są tu trzy punkty. Po pierwsze, żadna aplikacja, żaden katalog i żadna inna pula nie mogą ponownie ustawić skończonego limitu; php.net wyraźnie wymaga tej kontroli. Po drugie, usługa z memory_limit=-1 potrzebuje limitu z zewnątrz, przez kontener lub cgroup, według php.net najlepiej tak, aby kończony był tylko dotknięty worker. RLIMIT_AS i RLIMIT_DATA są według php.net nieodpowiednie. Po trzecie, php.net zaleca limity na serwerze WWW lub proxy dla rozmiaru żądania, liczby pól, części multipart, nagłówków i głębokości zagnieżdżenia.

Ważne

memory_limit=-1 bez limitu pamięci z zewnątrz może przy dużym obciążeniu całkowicie sparaliżować serwer. Proszę wprowadzać środki przejściowe tylko jako pakiet i tylko do czasu aktualizacji.

Jak ustawić limit za pomocą systemd, opisuje artykuł Limity pamięci z systemd i cgroups; rozwiązanie trwałe na co dzień opisuje Prawidłowe ustawianie memory_limit.

Jak konkretnie pomaga PHP SOS?

W ramach usługi poprawek PHP postępujemy tak:

  • Sprawdzenie stanu: Rejestrujemy każdą wersję PHP na serwerze, osobno dla CLI, FPM i modułu Apache, i testujemy, czy działa wersja z poprawką.
  • Uporządkowanie limitów pamięci: Sprawdzamy, gdzie wszędzie ustawiany jest memory_limit, i konfigurujemy limity puli oraz systemu tak, aby do siebie pasowały.
  • Zabezpieczenie okresu przejściowego: Tam, gdzie aktualizacja jest jeszcze zablokowana, wprowadzamy środki php.net w całości i zablokowane przed nadpisaniem.
  • Dostarczenie dowodu: Otrzymują Państwo na piśmie wersje, stany pakietów i konfigurację przed ingerencją i po niej.

Częste pytania

Co oznacza komunikat „Allowed memory size exhausted”?

Skrypt chciał zająć więcej pamięci, niż pozwala memory_limit. PHP przerywa wtedy bieżące żądanie błędem krytycznym. Inne żądania i sam serwer działają dalej.

Czy memory_limit obowiązuje dla całego serwera?

Nie, limit obowiązuje dla każdego procesu PHP i żądania. Jeśli działa 20 workerów FPM z limitem 256 MB każdy, teoretycznie mogą razem zająć ponad 5 GB. Limit całkowity trzeba więc ustawić przez rozmiar puli, systemd lub kontener.

Czym jest memory_hard_limit_percent?

To nowa dyrektywa, która pojawiła się w PHP 8.5.12, 8.4.27, 8.3.36 i 8.2.35. Ustawia twardy limit jako wartość procentową memory_limit, który nigdy nie jest przekraczany. Sam memory_limit jest od tego czasu limitem miękkim.

Czy memory_limit=-1 chroni przed PHP2Shell?

Tylko częściowo. php.net podaje to ustawienie jako środek przejściowy przeciw CVE-2026-109618, wyraźnie bez pełnej ochrony. Wymaga ono koniecznie limitu pamięci z zewnątrz, a aktualizacja pozostaje obowiązkowa.

Źródła

Kontakt natychmiast

Liczy się każda godzina. Proszę napisać do nas teraz.

Proszę krótko opisać system, wersję PHP i obserwacje. Skontaktujemy się z Państwem, ustalimy sytuację i będziemy pracować zdalnie, aż incydent zostanie zamknięty i udokumentowany.

Natychmiastowa pomoc