php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #81585 cached_chunks are not counted to real_size on shutdown
Submitted: 2021-11-01 19:59 UTC Modified: 2021-12-09 14:37 UTC
From: xtpd17 at gmail dot com Assigned: cmb (profile)
Status: Closed Package: Scripting Engine problem
PHP Version: 7.4 OS: *
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: xtpd17 at gmail dot com
New email:
PHP Version: OS:

 

 [2021-11-01 19:59 UTC] xtpd17 at gmail dot com
Description:
------------
(System: Win7x64, last Apache 2.4 x32, php x32)

If your script creates array of small strings (not more than 2mb approx.) and gets all amount of available memory (memory_limit could be set or left "-1") and then stops on "Fatal error: Allowed memory exhausted", it doesn't free all memory, so size of httpd.exe remains huge. If you run this script several times, size of httpd.exe grows and grows, and at some point your _another_ "innocent" scripts fails when trying to alloc relatively small chunk of memory with "Out of memory" (though their limits were not exceeded).

Steps to reproduce: reload script in browser several times until it fails. You'll see growing initial size of httpd, then fail.

Notable things:
- memory leaks only when we create small string (<2mb)
- big (or not set) memory_limit spends memory faster
- it looks like E_USER_ERROR also keeps memory busy (see commented strings in script)
- at second run memory_get_usage(true) is close to zero, memory_get_usage() grows.
- in fact we have "out of memory" error already in the second run, but I thought it would be more clear to have separate block to demonstrate leak.
- feel free to experiment with settings values in script.


TEST RESULTS (memory_limit = 1500M):

FIRST RUN ----------------------

apache size: 16 mb, memory usage/real usage: 0/2 mb (0 items in array, 0 mb stored)
apache size: 115 mb, memory usage/real usage: 98/100 mb (49 items in array, 98 mb stored)
apache size: 213 mb, memory usage/real usage: 196/198 mb (98 items in array, 196 mb stored)
...
Fatal error: Allowed memory size of 1572864000 bytes exhausted (tried to allocate 2093056 bytes) in test_leak.php on line 58
before crash:
apache size: 1517 mb, memory usage/real usage: 1495/1500 mb (749 items in array, 1494 mb stored)


--- SECOND RUN ------------------------------

apache size: 766 mb, memory usage/real usage: 0/2 mb (0 items in array, 0 mb stored)
apache size: 766 mb, memory usage/real usage: 98/2 mb (49 items in array, 98 mb stored)
apache size: 766 mb, memory usage/real usage: 196/2 mb (98 items in array, 196 mb stored)
///
Fatal error: Out of memory (allocated 1113587712) (tried to allocate 2093056 bytes) in test_leak.php on line 58
(WE HAVE OUT OF MEMORY ERROR ALREADY AT THIS POINT)

before crash:
apache size: 1828 mb, memory usage/real usage: 1805/1062 mb (904 items in array, 1804 mb stored)

--- THEN YOU GET 1297 mb of apache, then 1562...

--- AND THEN... FAIL RUN -------------------------------------

apache size: 1694 mb, memory usage/real usage: 0/2 mb (0 items in array, 0 mb stored)
apache overloaded (1694 mb), trying to alloc 200 mb

Fatal error: Out of memory (allocated 2097152) (tried to allocate 209715224 bytes) in test_leak.php on line 51

before crash:
apache size: 1694 mb, memory usage/real usage: 0/2 mb (0 items in array, 0 mb stored)



Test script:
---------------
https://pastebin.com/qh8hneTg


Patches

Add a Patch

Pull Requests

Pull requests:

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2021-11-29 17:53 UTC] cmb@php.net
-Package: Reproducible crash +Package: Apache related -Operating System: Windows 7 x64 +Operating System: Windows -PHP Version: 8.0.12 +PHP Version: 7.4 -Assigned To: +Assigned To: cmb
 [2021-11-29 17:53 UTC] cmb@php.net
First, the fact that not all request memory is freed on shutdown,
is a deliberate design decisions[1].  The reasoning is that later
reallocation of the memory from the OS is expensive.

Second, the total amount of memory allocated (httpd_mb) can easily
exceed PHP's memory_limit for a multi-threaded environment such as
Apache mpm_winnt.  If I set `ThreadsPerChild 1` (note there is only
one child for mpm_winnt), I still get four threads (might be a
hard coded limit).  Then the maximum amount of memory allocated is
about four times the memory_limit, so this doesn't look like a
bug.  However, the reported values for memory_get_usage() are not
what I would have expected; it seems that memory_get_usage(true)
reports the per thread values, but memory_get_usage(false) return
the process values.  I'll have a closer look.

[1] <https://github.com/php/php-src/blob/php-7.4.26/Zend/zend_alloc.c#L2264>
 [2021-12-07 14:21 UTC] cmb@php.net
-Summary: Memory leaks when creating short string arrrays +Summary: Reused cached_chunks are not counted to heap size -Status: Assigned +Status: Analyzed -Package: Apache related +Package: Scripting Engine problem -Operating System: Windows +Operating System: *
 [2021-12-07 14:21 UTC] cmb@php.net
Setting `ThreadsPerChild 1` actually yields only 1 worker thread;
the other threads are ancilliary.

Anyhow, the problem is that we don't count reused cached_chunks
(i.e. those which are retained between requests) to the size of
the heap, so the memory stats are wrong, and the memory_limit is
not properly heeded.  The latter causes the reported memory leak,
because the script is trying to exploit that.

This is not particularly related to Windows and Apache, but
happens for all web environments, i.e. whenever a process or
thread serves multiple consecutive requests (and chunks are
cached).
 [2021-12-07 14:28 UTC] cmb@php.net
The following pull request has been associated:

Patch Name: Fix #81585: Reused cached_chunks are not counted to heap size
On GitHub:  https://github.com/php/php-src/pull/7732
Patch:      https://github.com/php/php-src/pull/7732.patch
 [2021-12-09 14:37 UTC] cmb@php.net
-Summary: Reused cached_chunks are not counted to heap size +Summary: cached_chunks are not counted to real_size on shutdown
 [2021-12-09 14:37 UTC] cmb@php.net
The following pull request has been associated:

Patch Name: Fix #81585: cached_chunks are not counted to real_size on shutdown
On GitHub:  https://github.com/php/php-src/pull/7745
Patch:      https://github.com/php/php-src/pull/7745.patch
 [2021-12-10 11:27 UTC] git@php.net
Automatic comment on behalf of cmb69
Revision: https://github.com/php/php-src/commit/5675ebe649352ffa89754defd87019223b88b4e5
Log: Fix #81585: cached_chunks are not counted to real_size on shutdown
 [2021-12-10 11:27 UTC] git@php.net
-Status: Analyzed +Status: Closed
 
PHP Copyright © 2001-2022 The PHP Group
All rights reserved.
Last updated: Sat Jan 29 13:03:36 2022 UTC