]> granicus.if.org Git - php/commitdiff
Fix overflow in memory limit checks
authorNikita Popov <nikita.ppv@gmail.com>
Mon, 26 Aug 2019 08:23:23 +0000 (10:23 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Mon, 26 Aug 2019 08:25:30 +0000 (10:25 +0200)
Due to overflows in the memory limit checks, we were missing cases
where the allocation size was close to the address space size, and
caused an OOM condition rather than a memory limit error.

Zend/zend_alloc.c
ext/standard/tests/strings/wordwrap_memory_limit.phpt [new file with mode: 0644]

index 3744a83c84e46adc54b1f3ad71a7ebf9353ecb2d..3a43027346dba32aa846537622b23192daeb09ff 100644 (file)
@@ -980,7 +980,7 @@ get_chunk:
                                heap->cached_chunks = chunk->next;
                        } else {
 #if ZEND_MM_LIMIT
-                               if (UNEXPECTED(heap->real_size + ZEND_MM_CHUNK_SIZE > heap->limit)) {
+                               if (UNEXPECTED(ZEND_MM_CHUNK_SIZE > heap->limit - heap->real_size)) {
                                        if (zend_mm_gc(heap)) {
                                                goto get_chunk;
                                        } else if (heap->overflow == 0) {
@@ -1484,8 +1484,8 @@ static void *zend_mm_realloc_heap(zend_mm_heap *heap, void *ptr, size_t size, si
                                }
                        } else /* if (new_size > old_size) */ {
 #if ZEND_MM_LIMIT
-                               if (UNEXPECTED(heap->real_size + (new_size - old_size) > heap->limit)) {
-                                       if (zend_mm_gc(heap) && heap->real_size + (new_size - old_size) <= heap->limit) {
+                               if (UNEXPECTED(new_size - old_size > heap->limit - heap->real_size)) {
+                                       if (zend_mm_gc(heap) && new_size - old_size <= heap->limit - heap->real_size) {
                                                /* pass */
                                        } else if (heap->overflow == 0) {
 #if ZEND_DEBUG
@@ -1730,8 +1730,8 @@ static void *zend_mm_alloc_huge(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_D
        void *ptr;
 
 #if ZEND_MM_LIMIT
-       if (UNEXPECTED(heap->real_size + new_size > heap->limit)) {
-               if (zend_mm_gc(heap) && heap->real_size + new_size <= heap->limit) {
+       if (UNEXPECTED(new_size > heap->limit - heap->real_size)) {
+               if (zend_mm_gc(heap) && new_size <= heap->limit - heap->real_size) {
                        /* pass */
                } else if (heap->overflow == 0) {
 #if ZEND_DEBUG
diff --git a/ext/standard/tests/strings/wordwrap_memory_limit.phpt b/ext/standard/tests/strings/wordwrap_memory_limit.phpt
new file mode 100644 (file)
index 0000000..fb0cc5c
--- /dev/null
@@ -0,0 +1,18 @@
+--TEST--
+No overflow should occur during the memory_limit check for wordwrap()
+--SKIPIF--
+<?php
+if (getenv("USE_ZEND_ALLOC") === "0") die("skip Zend MM disabled");
+?>
+--INI--
+memory_limit=128M
+--FILE--
+<?php
+
+$str = str_repeat('x', 65534);
+$str2 = str_repeat('x', 65535);
+wordwrap($str, 1, $str2);
+
+?>
+--EXPECTF--
+Fatal error: Allowed memory size of 134217728 bytes exhausted%s(tried to allocate %d bytes) in %s on line %d