# memory_limit i Out-of-Memory: co dzieje się w środku

> PHP Out of Memory zrozumiale: jak liczy memory_limit, co dzieje się wewnętrznie przy przerwaniu i co to oznacza dla CVE-2026-109618 oraz Państwa serwerów.

Źródło: https://phpsos.de/pl/php/out-of-memory/  
Stan: 25.10.2026  
Dostawca: PHP SOS, Pomoc awaryjna PHP i bezpieczeństwo serwerów. Natychmiastowa pomoc przez WhatsApp: https://wa.me/491736494914?text=Dzie%C5%84%20dobry%2C%20PHP%20SOS%2C%20potrzebujemy%20pomocy%20z%20naszym%20serwerem%20PHP.%20Firma%3A%20%E2%80%A6%20Strona%20internetowa%3A%20%E2%80%A6

**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
<?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](https://phpsos.de/pl/php/speicherverwaltung/).

## 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](https://phpsos.de/pl/php2shell/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:

```bash
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](https://phpsos.de/pl/php2shell/php-version-pruefen/). 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:

```ini
; 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](https://phpsos.de/pl/blog/speichergrenzen-systemd-cgroups/); rozwiązanie trwałe na co dzień opisuje [Prawidłowe ustawianie memory_limit](https://phpsos.de/pl/blog/memory-limit-richtig-setzen/).

## Jak konkretnie pomaga PHP SOS?

W ramach [usługi poprawek PHP](https://phpsos.de/pl/leistungen/php-patch-service/) 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

- [Podręcznik PHP: ustawienia rdzenia (memory_limit, memory_hard_limit_percent)](https://www.php.net/manual/en/ini.core.php)
- [Podręcznik PHP: memory_get_usage()](https://www.php.net/manual/en/function.memory-get-usage.php)
- [php.net: komunikaty o bezpieczeństwie z 21.10.2026](https://www.php.net/)
