Lyhyesti: PHP:n Out-of-Memory-virhe syntyy, kun komentosarja yrittää varata enemmän muistia kuin asetus memory_limit sallii (oletus: 128 Mt pyyntöä kohti). PHP keskeyttää silloin pyynnön vakavaan virheeseen ja siivoaa jälkensä. Juuri tämä keskeytys- ja siivousreitti oli virheellinen haavoittuvuudessa CVE-2026-109618, joka on toinen PHP2Shell-aukoista. Versioista PHP 8.5.12, 8.4.27, 8.3.36 ja 8.2.35 lähtien memory_limit on pehmeä raja, jota täydentää kova raja memory_hard_limit_percent.
Mitä ”Allowed memory size exhausted” tarkoittaa?
Tuttu virheilmoitus kuuluu suunnilleen: ”Allowed memory size of 134217728 bytes exhausted (tried to allocate …)”. Ensimmäinen luku on asetettu raja tavuina, tässä 128 Mt. Komentosarja on siis yrittänyt varata muistia tämän rajan yli, esimerkiksi suuren tiedoston lataamisessa, valtavassa tietokantakyselyssä tai päättymättömässä silmukassa.
Päättäjälle tärkeää: raja koskee yhtä PHP-prosessia ja yhtä pyyntöä, ei koko palvelinta. Se suojaa palvelinta siltä, että yksittäinen virheellinen komentosarja käyttää kaiken muistin. Kokonaiskuorma syntyy vasta samanaikaisesti käynnissä olevien workerien määrästä. 20 PHP-FPM-workerilla, joilla kullakin on 256 Mt:n raja, se on laskennallisesti yli 5 Gt.
Miten PHP laskee muistia
PHP hallitsee pyyntöjen muistia omalla muistinhallinnallaan, Zend Memory Managerilla. Se hakee suurempia lohkoja käyttöjärjestelmältä ja jakaa ne sisäisesti muuttujille, taulukoille ja olioille. Tätä varattua arvoa vasten PHP tarkistaa memory_limit-rajan. Muisti, jonka laajennus pyytää suoraan käyttöjärjestelmältä, ei käsikirjan mukaan näy tässä laskennassa.
Arvot voi tehdä näkyviksi koodissa:
<?php
echo memory_get_usage(), PHP_EOL; // komentosarjan tällä hetkellä varaama muisti
echo memory_get_usage(true), PHP_EOL; // muistinhallinnan varaama; tätä vasten memory_limit tarkistetaan
$liste = range(1, 200000);
echo memory_get_peak_usage(), PHP_EOL; // tähänastinen huippuarvo
unset($liste);
memory_reset_peak_usage(); // huippuarvon nollaus, versiosta PHP 8.2 lähtien
Miten viitelaskenta ja roskienkeruu vapauttavat muistin takaisin, selittää sivu Muistinhallinta PHP:ssä.
Mitä Out-of-Memoryssa tapahtuu sisäisesti
Yksinkertaistettuna muistin keskeytys etenee näissä vaiheissa:
- Komentosarja pyytää muistia, esimerkiksi uutta taulukon alkiota varten tai pyyntödataa luettaessa.
- Muistinhallinta tarkistaa, ylittääkö pyyntö rajan.
- Jos ylittää, PHP laukaisee vakavan virheen ja poistuu käynnissä olevasta käsittelystä heti, myös kesken puoliksi tehdyn tehtävän.
- Rekisteröidyt shutdown-funktiot suoritetaan vielä; monet sovellukset kirjaavat virheen niiden avulla.
- Lopuksi PHP siivoaa pyynnön ja vapauttaa sen muistin. Worker on sen jälkeen valmis seuraavaa pyyntöä varten.
Arka kohta on vaihe 5. Siivouksen on selvittävä tietorakenteista, jotka olivat keskeytyshetkellä vasta puoliksi luotuja. Tavallista muistin keskeytystä ei PHP-hankkeen tietoturvakäytännön mukaan pidetä tietoturva-aukkona, kunhan raja hylkää pyynnön ja vain käynnissä oleva pyyntö päättyy. Haavoittuvuudessa CVE-2026-109618 juuri tämä oletus oli rikki: php.netin mukaan Out-of-Memory-tila saattoi keskeyttää PHP:n, kun sisäinen pyyntödata oli vain osittain alustettu, ja siivous käsitteli virheellistä dataa kelvollisena. Hyväksikäytön yksityiskohtia emme julkaise; aukon luokitus löytyy sivulta CVE-2026-109618.
Uusi pehmeä ja kova muistiraja
21.10.2026 julkaistuilla tietoturvajulkaisuilla PHP-hanke muutti toimintaa. Käsikirjan mukaan memory_limit on siitä lähtien pehmeä raja: pyynnön, joka ylittää sen, annetaan useimmissa tapauksissa vielä päättyä ennen kuin vakava virhe seuraa. Erillinen kova raja määrää, kuinka pitkälle se saa mennä.
| Direktiivi | Oletus | Muutettavissa | Merkitys |
|---|---|---|---|
memory_limit |
128M | INI_ALL | Muistiraja pyyntöä kohti, -1 = ei rajaa |
memory_hard_limit_percent |
ei asetettu | INI_ALL | Kova raja prosentteina memory_limit-arvosta, uusi versioista 8.5.12, 8.4.27, 8.3.36, 8.2.35 lähtien |
post_max_size |
8M | INI_PERDIR | POST-datan enimmäiskoko |
upload_max_filesize |
2M | INI_PERDIR | Ladatun tiedoston enimmäiskoko |
Esimerkki käsikirjasta: kun memory_limit=128M ja memory_hard_limit_percent=150, kova raja on 192 Mt. Käsikirja mainitsee direktiivin samalla tunnistusmerkkinä: jos PHP-versio ei tunne sitä, se on alttiina haavoittuvuuksille CVE-2026-109618 ja CVE-2026-109619. Nopea testi jokaiselle asennetulle PHP-versiolle:
php -r 'var_dump(ini_get("memory_hard_limit_percent"));'
# bool(false) tarkoittaa: direktiivi on tuntematon
Jakelujen paketeissa, joissa on takaisinporttaus (backport), paketin muutosloki on edelleen ratkaiseva todiste, kuten sivulla PHP-version ja korjaustilan oikea tarkistus kuvataan. Tarkistakaa lisäksi PHP-FPM-binääri erikseen, sillä sen versio voi olla eri kuin komentorivin.
Muistirajat ja PHP2Shell: mitä ylläpitäjien on nyt huomioitava
php.netin väliaikaiset toimenpiteet haavoittuvuuteen CVE-2026-109618 pyörivät lähes kaikki muistin ja pyyntökokojen ympärillä. Ne pienentävät riskiä mutta eivät korvaa päivitystä:
; FPM-pooli, esim. /etc/php/8.3/fpm/pool.d/www.conf
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M ; 32M, jos lataukset ovat tarpeen
php_admin_value[upload_max_filesize] = 6M ; pidä post_max_size-arvon alapuolella
Kolme kohtaa on ratkaisevia. Ensinnäkin mikään sovellus, hakemisto tai toinen pooli ei saa asettaa takaisin rajallista arvoa; php.net vaatii tätä valvontaa nimenomaisesti. Toiseksi palvelu, jolla on memory_limit=-1, tarvitsee rajan ulkopuolelta, kontin tai cgroupin kautta, php.netin mukaan parhaiten niin, että vain kyseinen worker päätetään. RLIMIT_AS ja RLIMIT_DATA ovat php.netin mukaan sopimattomia. Kolmanneksi php.net suosittelee verkkopalvelimen tai välityspalvelimen rajoja pyynnön koolle, kenttien määrälle, multipart-osille, otsakkeille ja sisäkkäisyyden syvyydelle.
memory_limit=-1 ilman ulkopuolelta asetettua muistirajaa voi lamauttaa palvelimen kokonaan suuren kuorman aikana. Ottakaa väliaikaiset toimenpiteet käyttöön vain pakettina ja vain päivitykseen asti.
Miten rajan asettaa systemd:llä, kerrotaan artikkelissa Muistirajat systemd:llä ja cgroupeilla; arjen pysyvän ratkaisun kuvaa memory_limit oikein asetettuna.
Miten PHP SOS auttaa konkreettisesti?
PHP-korjauspalvelun kautta toimimme näin:
- Kanta tarkistetaan: Kartoitamme jokaisen PHP-version palvelimella erikseen CLI:n, FPM:n ja Apache-moduulin osalta ja testaamme, onko korjattu versio käytössä.
- Muistirajat järjestetään: Tarkistamme, missä kaikkialla
memory_limitasetetaan, ja järjestämme pooli- ja järjestelmärajat niin, että ne sopivat yhteen. - Siirtymä turvataan: Missä päivitys on vielä estynyt, otamme php.netin toimenpiteet käyttöön täydellisinä ja ylikirjoitukselta lukittuina.
- Todiste toimitetaan: Saatte versiot, pakettitilanteet ja konfiguraation ennen toimenpidettä ja sen jälkeen kirjallisesti.
Usein kysytyt kysymykset
Mitä ilmoitus ”Allowed memory size exhausted” tarkoittaa?
Komentosarja yritti varata enemmän muistia kuin memory_limit sallii. PHP keskeyttää käynnissä olevan pyynnön silloin vakavaan virheeseen. Muut pyynnöt ja palvelin itse jatkavat toimintaansa.
Koskeeko memory_limit koko palvelinta?
Ei, raja koskee yhtä PHP-prosessia ja pyyntöä kerrallaan. Jos käynnissä on 20 FPM-workeria, joilla kullakin on 256 Mt:n raja, ne voivat yhdessä teoriassa varata yli 5 Gt. Kokonaisraja on siksi asetettava poolin koon, systemd:n tai kontin kautta.
Mikä on memory_hard_limit_percent?
Uusi direktiivi, joka tuli versioiden PHP 8.5.12, 8.4.27, 8.3.36 ja 8.2.35 mukana. Se asettaa kovan rajan memory_limit-arvon prosenttiosuutena, jota ei koskaan ylitetä. memory_limit itse on siitä lähtien pehmeä raja.
Suojaako memory_limit=-1 PHP2Shelliltä?
Vain osittain. php.net mainitsee asetuksen väliaikaisena toimenpiteenä haavoittuvuutta CVE-2026-109618 vastaan, nimenomaan ilman täydellistä suojaa. Se vaatii ehdottomasti ulkopuolelta asetetun muistirajan, ja päivitys on edelleen pakollinen.