Εν συντομία: Ένα σφάλμα PHP out-of-memory προκύπτει όταν ένα σενάριο θέλει να ζητήσει περισσότερη μνήμη από όση επιτρέπει η ρύθμιση memory_limit (προεπιλογή: 128 MB ανά αίτημα). Η PHP διακόπτει τότε το αίτημα με μοιραίο σφάλμα και καθαρίζει. Ακριβώς αυτή η διαδρομή διακοπής και καθαρισμού ήταν ελαττωματική στην CVE-2026-109618, μία από τις δύο ευπάθειες PHP2Shell. Από τις PHP 8.5.12, 8.4.27, 8.3.36 και 8.2.35 το memory_limit είναι ένα μαλακό όριο, το οποίο συμπληρώνεται από το σκληρό όριο memory_hard_limit_percent.
Τι σημαίνει το «Allowed memory size exhausted»;
Το γνωστό μήνυμα σφάλματος έχει περίπου τη μορφή: «Allowed memory size of 134217728 bytes exhausted (tried to allocate …)». Ο πρώτος αριθμός είναι το ρυθμισμένο όριο σε byte, εδώ 128 MB. Ένα σενάριο προσπάθησε επομένως να καταλάβει μνήμη πέρα από αυτό το όριο, για παράδειγμα κατά τη φόρτωση ενός μεγάλου αρχείου, ενός τεράστιου ερωτήματος βάσης δεδομένων ή λόγω ενός ατέρμονου βρόχου.
Σημαντικό για όσους αποφασίζουν: το όριο ισχύει ανά διεργασία PHP και ανά αίτημα, όχι για ολόκληρο τον διακομιστή. Προστατεύει τον διακομιστή από το να εξαντλήσει ένα μεμονωμένο ελαττωματικό σενάριο όλη τη μνήμη. Το συνολικό φορτίο προκύπτει μόνο από τον αριθμό των workers που εκτελούνται ταυτόχρονα. Με 20 workers PHP-FPM και όριο 256 MB ο καθένας, αυτό υπολογιστικά ξεπερνά τα 5 GB.
Πώς μετρά η PHP τη μνήμη
Η PHP διαχειρίζεται τη μνήμη για τα αιτήματα μέσω ενός δικού της διαχειριστή μνήμης, του Zend Memory Manager. Παίρνει μεγαλύτερα μπλοκ από το λειτουργικό σύστημα και τα μοιράζει εσωτερικά σε μεταβλητές, πίνακες και αντικείμενα. Με αυτή την τιμή που έχει δεσμευτεί ελέγχει η PHP το memory_limit. Η μνήμη που μια επέκταση ζητά απευθείας από το λειτουργικό σύστημα δεν εμφανίζεται, σύμφωνα με το εγχειρίδιο, σε αυτή τη μέτρηση.
Μπορείτε να κάνετε τις τιμές ορατές μέσα στον κώδικα:
<?php
echo memory_get_usage(), PHP_EOL; // μνήμη που καταλαμβάνει αυτή τη στιγμή το σενάριο
echo memory_get_usage(true), PHP_EOL; // δεσμευμένη από τον διαχειριστή μνήμης· με αυτήν ελέγχεται το memory_limit
$liste = range(1, 200000);
echo memory_get_peak_usage(), PHP_EOL; // μέγιστη τιμή μέχρι τώρα
unset($liste);
memory_reset_peak_usage(); // επαναφορά της μέγιστης τιμής, από την PHP 8.2
Πώς η μέτρηση αναφορών και η garbage collection ελευθερώνουν ξανά τη μνήμη, εξηγεί η σελίδα Διαχείριση μνήμης στην PHP.
Τι συμβαίνει στο εσωτερικό σε ένα out-of-memory
Απλοποιημένα, μια διακοπή λόγω μνήμης εξελίσσεται σε αυτά τα βήματα:
- Το σενάριο ζητά μνήμη, για παράδειγμα για ένα νέο στοιχείο πίνακα ή κατά την ανάγνωση δεδομένων του αιτήματος.
- Ο διαχειριστής μνήμης ελέγχει αν το αίτημα υπερβαίνει το όριο.
- Αν ναι, η PHP προκαλεί ένα μοιραίο σφάλμα και εγκαταλείπει αμέσως την τρέχουσα επεξεργασία, ακόμη και στη μέση μιας μισοτελειωμένης εργασίας.
- Οι καταχωρισμένες συναρτήσεις shutdown εκτελούνται ακόμη· με αυτές πολλές εφαρμογές καταγράφουν το σφάλμα.
- Στο τέλος η PHP καθαρίζει το αίτημα και ελευθερώνει τη μνήμη του. Ο worker είναι μετά έτοιμος για το επόμενο αίτημα.
Το λεπτό σημείο είναι το βήμα 5. Ο καθαρισμός πρέπει να μπορεί να χειριστεί δομές δεδομένων που κατά τη διακοπή είχαν δημιουργηθεί μόνο εν μέρει. Μια συνηθισμένη διακοπή λόγω μνήμης δεν θεωρείται σύμφωνα με την πολιτική ασφαλείας του έργου PHP ευπάθεια ασφαλείας, εφόσον το όριο απορρίπτει το αίτημα και τερματίζεται μόνο το τρέχον αίτημα. Στην CVE-2026-109618 αυτή ακριβώς η παραδοχή παραβιαζόταν: σύμφωνα με το php.net, η κατάσταση out-of-memory μπορούσε να διακόψει την PHP ενώ τα εσωτερικά δεδομένα του αιτήματος ήταν μόνο εν μέρει αρχικοποιημένα και ο καθαρισμός αντιμετώπιζε τότε άκυρα δεδομένα ως έγκυρα. Λεπτομέρειες της εκμετάλλευσης δεν δημοσιεύουμε· την κατάταξη της ευπάθειας θα βρείτε στη σελίδα CVE-2026-109618.
Το νέο μαλακό και σκληρό όριο μνήμης
Με τις εκδόσεις ασφαλείας της 21.10.2026 το έργο PHP άλλαξε τη συμπεριφορά. Σύμφωνα με το εγχειρίδιο, το memory_limit είναι έκτοτε ένα μαλακό όριο: ένα αίτημα που το υπερβαίνει επιτρέπεται στις περισσότερες περιπτώσεις να ολοκληρωθεί πριν ακολουθήσει το μοιραίο σφάλμα. Ένα ξεχωριστό σκληρό όριο περιορίζει μέχρι πού μπορεί να φτάσει αυτό.
| Οδηγία | Προεπιλογή | Τροποποιήσιμη | Σημασία |
|---|---|---|---|
memory_limit |
128M | INI_ALL | Όριο μνήμης ανά αίτημα, -1 = χωρίς όριο |
memory_hard_limit_percent |
δεν έχει οριστεί | INI_ALL | Σκληρό όριο ως ποσοστό του memory_limit, νέο από τις 8.5.12, 8.4.27, 8.3.36, 8.2.35 |
post_max_size |
8M | INI_PERDIR | Μέγιστο μέγεθος των δεδομένων POST |
upload_max_filesize |
2M | INI_PERDIR | Μέγιστο μέγεθος ενός αρχείου που μεταφορτώνεται |
Ένα παράδειγμα από το εγχειρίδιο: με memory_limit=128M και memory_hard_limit_percent=150, το σκληρό όριο είναι στα 192 MB. Το εγχειρίδιο αναφέρει την οδηγία ταυτόχρονα ως γνώρισμα αναγνώρισης: αν μια έκδοση PHP δεν την γνωρίζει, επηρεάζεται από τις CVE-2026-109618 και CVE-2026-109619. Ένας γρήγορος έλεγχος για κάθε εγκατεστημένη έκδοση PHP:
php -r 'var_dump(ini_get("memory_hard_limit_percent"));'
# το bool(false) σημαίνει: άγνωστη οδηγία
Στα πακέτα από διανομές με backport, το changelog του πακέτου παραμένει το καθοριστικό αποδεικτικό στοιχείο, όπως περιγράφεται στη σελίδα Σωστός έλεγχος έκδοσης PHP και επιπέδου ενημέρωσης. Ελέγξτε επιπλέον ξεχωριστά το δυαδικό αρχείο PHP-FPM, επειδή μπορεί να έχει διαφορετική έκδοση από τη γραμμή εντολών.
Όρια μνήμης και PHP2Shell: τι πρέπει να προσέξουν τώρα οι διαχειριστές
Τα προσωρινά μέτρα του php.net για την CVE-2026-109618 αφορούν σχεδόν όλα τη μνήμη και τα μεγέθη των αιτημάτων. Μειώνουν τον κίνδυνο, αλλά δεν αντικαθιστούν την ενημέρωση:
; Pool FPM, π.χ. /etc/php/8.3/fpm/pool.d/www.conf
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M ; 32M, όταν χρειάζονται μεταφορτώσεις
php_admin_value[upload_max_filesize] = 6M ; να διατηρείται κάτω από το post_max_size
Τρία σημεία είναι καθοριστικά. Πρώτον, καμία εφαρμογή, κανένας κατάλογος και κανένα άλλο pool δεν επιτρέπεται να ορίσει ξανά πεπερασμένο όριο· το php.net απαιτεί ρητά αυτόν τον έλεγχο. Δεύτερον, μια υπηρεσία με memory_limit=-1 χρειάζεται όριο από έξω, μέσω container ή cgroup, κατά το php.net καλύτερα έτσι ώστε να τερματίζεται μόνο ο επηρεαζόμενος worker. Τα RLIMIT_AS και RLIMIT_DATA είναι κατά το php.net ακατάλληλα. Τρίτον, το php.net συνιστά όρια στον διακομιστή ιστού ή στον proxy για το μέγεθος του αιτήματος, τον αριθμό των πεδίων, τα τμήματα multipart, τις κεφαλίδες και το βάθος φωλιάσματος.
Το memory_limit=-1 χωρίς όριο μνήμης από έξω μπορεί να παραλύσει εντελώς έναν διακομιστή υπό υψηλό φορτίο. Ορίστε τα προσωρινά μέτρα μόνο ως πακέτο και μόνο μέχρι την ενημέρωση.
Πώς ορίζετε το όριο με το systemd, αναφέρεται στη σελίδα Όρια μνήμης με systemd και cgroups· τη μόνιμη λύση για την καθημερινότητα περιγράφει το άρθρο Σωστός ορισμός του memory_limit.
Πώς βοηθά συγκεκριμένα η PHP SOS;
Μέσω της υπηρεσίας patch PHP ακολουθούμε την εξής πορεία:
- Έλεγχος του υπάρχοντος: Καταγράφουμε κάθε έκδοση PHP στον διακομιστή, χωριστά για CLI, FPM και module Apache, και δοκιμάζουμε αν εκτελείται η έκδοση με τη διόρθωση.
- Τακτοποίηση των ορίων μνήμης: Ελέγχουμε πού ορίζεται παντού το
memory_limitκαι ρυθμίζουμε όρια pool και συστήματος ώστε να ταιριάζουν μεταξύ τους. - Ασφάλιση της μετάβασης: Όπου η ενημέρωση εμποδίζεται ακόμη, εφαρμόζουμε πλήρως τα μέτρα του php.net, κλειδωμένα απέναντι σε παράκαμψη.
- Παράδοση αποδεικτικών: Λαμβάνετε γραπτώς εκδόσεις, επίπεδα πακέτων και διαμόρφωση πριν και μετά την παρέμβαση.
Συχνές ερωτήσεις
Τι σημαίνει το μήνυμα «Allowed memory size exhausted»;
Ένα σενάριο ήθελε να ζητήσει περισσότερη μνήμη από όση επιτρέπει το memory_limit. Η PHP διακόπτει τότε το τρέχον αίτημα με μοιραίο σφάλμα. Τα άλλα αιτήματα και ο ίδιος ο διακομιστής συνεχίζουν να λειτουργούν.
Ισχύει το memory_limit για ολόκληρο τον διακομιστή;
Όχι, το όριο ισχύει ανά διεργασία PHP και αίτημα. Αν εκτελούνται 20 workers FPM με όριο 256 MB ο καθένας, μπορούν θεωρητικά να καταλάβουν μαζί πάνω από 5 GB. Το συνολικό όριο πρέπει επομένως να ορίζεται μέσω του μεγέθους του pool, του systemd ή του container.
Τι είναι το memory_hard_limit_percent;
Μια νέα οδηγία που ήρθε με τις PHP 8.5.12, 8.4.27, 8.3.36 και 8.2.35. Ορίζει ένα σκληρό όριο ως ποσοστό του memory_limit, το οποίο δεν ξεπερνιέται ποτέ. Το ίδιο το memory_limit είναι έκτοτε ένα μαλακό όριο.
Προστατεύει το memory_limit=-1 από το PHP2Shell;
Μόνο εν μέρει. Το php.net αναφέρει τη ρύθμιση ως προσωρινό μέτρο κατά της CVE-2026-109618, ρητά χωρίς πλήρη προστασία. Απαιτεί αναγκαστικά ένα όριο μνήμης από έξω και η ενημέρωση παραμένει υποχρεωτική.