From 1f0f6338cca7bd4f8dc4048fc68d17907f0a9f60 Mon Sep 17 00:00:00 2001 From: Peter van Dommelen Date: Sun, 30 May 2021 19:21:38 +0200 Subject: [PATCH 1/3] Fixed application of memory limit in tests/lang/bug45392. Marked as XFAIL. This old bugfix is not working as intended. The memory limit in main's `PG(memory_limit)` differs from the one in zend_alloc. In zend_alloc the `AG(mm_heap)->limit` is defined as `max(passed_value, ZEND_MM_CHUNK_SIZE)`. The check made in an unclean shutdown will never be true unless the memory limit is lower than ZEND_MM_CHUNK_SIZE, which happened to be the case in the test. https://bugs.php.net/bug.php?id=45392 https://github.com/php/php-src/commit/fcc0fdd125fdb9e1713f91d027fe07d680a0cf36 --- tests/lang/bug45392.phpt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/lang/bug45392.phpt b/tests/lang/bug45392.phpt index a9bdd40af2be0..5940f7bc39814 100644 --- a/tests/lang/bug45392.phpt +++ b/tests/lang/bug45392.phpt @@ -2,6 +2,8 @@ Bug #45392 (ob_start()/ob_end_clean() and memory_limit) --INI-- display_errors=stderr +--XFAIL-- +The issue has not yet been resolved. --SKIPIF-- Date: Sun, 30 May 2021 20:28:50 +0200 Subject: [PATCH 2/3] Increased memory limit for ext/standard/tests/streams/bug78902 Reducing the memory limit below the current memory has probably not been the intention of this test. --- ext/standard/tests/streams/bug78902.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/tests/streams/bug78902.phpt b/ext/standard/tests/streams/bug78902.phpt index 1216c89bc2747..f2ac5d9eeaab1 100644 --- a/ext/standard/tests/streams/bug78902.phpt +++ b/ext/standard/tests/streams/bug78902.phpt @@ -1,14 +1,14 @@ --TEST-- Bug #78902: Memory leak when using stream_filter_append --INI-- -memory_limit=512k +memory_limit=2M --FILE-- 0) { fputs($fp, str_pad('', min($chunk,$size))); From 27a15f55575dab638216f277fd5d3261904116ff Mon Sep 17 00:00:00 2001 From: Peter van Dommelen Date: Sun, 30 May 2021 19:30:24 +0200 Subject: [PATCH 3/3] Fix #81070: Validate new memory limit for integer underflow When the memory limit is reduced using an `ini_set("memory_limit", ..)` below the currently allocated memory, the out-of-memory check overflowed. Instead of implementing additional checks during allocation, `zend_set_memory_limit` now validates the new memory limit. When below the current memory usage the ini_set call will return a warning. --- Zend/tests/bug81070.phpt | 9 +++++++++ Zend/zend_alloc.c | 3 +++ main/main.c | 12 +++++++++--- 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 Zend/tests/bug81070.phpt diff --git a/Zend/tests/bug81070.phpt b/Zend/tests/bug81070.phpt new file mode 100644 index 0000000000000..0dc45056e3346 --- /dev/null +++ b/Zend/tests/bug81070.phpt @@ -0,0 +1,9 @@ +--TEST-- +Bug #81070 Setting memory limit to below current usage +--FILE-- + +--EXPECTF-- +Warning: Failed to set memory limit to 3145728 bytes (Current memory usage is %d bytes) in %s on line %d diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index f5fbee676db63..4720dfb682b47 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -2660,6 +2660,9 @@ ZEND_API char* ZEND_FASTCALL zend_strndup(const char *s, size_t length) ZEND_API int zend_set_memory_limit(size_t memory_limit) { #if ZEND_MM_LIMIT + if (UNEXPECTED(memory_limit < AG(mm_heap)->real_size)) { + return FAILURE; + } AG(mm_heap)->limit = (memory_limit >= ZEND_MM_CHUNK_SIZE) ? memory_limit : ZEND_MM_CHUNK_SIZE; #endif return SUCCESS; diff --git a/main/main.c b/main/main.c index b17bbd7fe7b0a..e675998575f15 100644 --- a/main/main.c +++ b/main/main.c @@ -296,12 +296,18 @@ static PHP_INI_MH(OnSetSerializePrecision) */ static PHP_INI_MH(OnChangeMemoryLimit) { + size_t value; if (new_value) { - PG(memory_limit) = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); + value = zend_atol(ZSTR_VAL(new_value), ZSTR_LEN(new_value)); } else { - PG(memory_limit) = Z_L(1)<<30; /* effectively, no limit */ + value = Z_L(1)<<30; /* effectively, no limit */ } - return zend_set_memory_limit(PG(memory_limit)); + if (zend_set_memory_limit(value) == FAILURE) { + zend_error(E_WARNING, "Failed to set memory limit to %zd bytes (Current memory usage is %zd bytes)", value, zend_memory_usage(true)); + return FAILURE; + } + PG(memory_limit) = value; + return SUCCESS; } /* }}} */