php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #49501 Memory leak ending up in Out of Memory errors (only with PaX/grsecurity)
Submitted: 2009-09-08 19:04 UTC Modified: 2009-10-06 14:51 UTC
From: romain dot riviere at gmail dot com Assigned:
Status: Not a bug Package: Scripting Engine problem
PHP Version: 5.*, 6 OS: Gentoo Linux 32bit / PaX
Private report: No CVE-ID:
 [2009-09-08 19:04 UTC] romain dot riviere at gmail dot com
Description:
------------
While trying to diagnose apparently random Out of Memory errors in various PHP scripts (including Drupal among others), I ended up using the code pasted below for testing purposes.

Browing the resulting page several times always results in an ever-increasing VSZ for apache processes (up to 4GB in my case) and eventually Out of Memory errors way before the script itself actually reaches the limit. The script was called with size=61 in my case (memory_limit=64M). On an 8GB system, it might take as many as 30 requests to recreate the bug. With a higher memory limit and value of the "size" parameter, it will probably happen much sooner.

PHP compiled with 

Reproduce code:
---------------
<?php
ini_set('display_errors',true);
if (isset($_GET['size']) && $_GET['size'] < 200) {
$mb = intval($_GET['size']);
}
else {
        $mb=5;
}
$var = '';
echo 'Memory limit: '.ini_get('memory_limit').'<br>';
for ($i=0; $i<=$mb; $i++) {
        $var.= str_repeat('a',1*1024*1024);
        echo memory_get_usage().'<br>';
}
echo 'String length: '. strlen($var);
?>


Expected result:
----------------
For the first few runs, the script correctly displays the memory usage and the value is consistent with the memory limit :

Memory limit: 64M
<snip>
String length: 65011712 

Actual result:
--------------
Memory limit: 64M
<snip>
Fatal error: Out of memory (allocated 61341696) at /var/tmp/portage/dev-lang/php-5.2.10/work/php-5.2.10/ext/standard/string.c:4599 (tried to allocate 1048577 bytes) in /home/acrm/OF-svn/2.0/test.php on line 14

Subsequent runs : 

Fatal error: Out of memory (allocated 262144) at /var/tmp/portage/dev-lang/php-5.2.10/work/php-5.2.10/ext/standard/string.c:4599 (tried to allocate 1048577 bytes) in /home/acrm/OF-svn/2.0/test.php on line 14

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2009-09-08 20:42 UTC] romain dot riviere at gmail dot com
The last line of the initial description should have read : PHP compiled --with-pic.

For the record, the problem appeared in Drupal and was reported as happening at zend_opcode.c:48. However, since it can obviously be reproduced with something as simple as a string, I believe it is a more general memory leak issue.
 [2009-09-08 20:57 UTC] rasmus@php.net
Are you saying you don't see it if you don't use --with-pic?

And I am curious, why do you want pic?  libphp5.so isn't a general-purpose shared library that can be linked into many different things.  It is a very targeted library compiled for a specific binary to link and as such compiling it pic just adds unnecessary overhead.
 [2009-09-09 03:23 UTC] romain dot riviere at gmail dot com
I'm mentioning pic because it is the only thing slightly out of the ordinary in this setup.

I am using pic because of disabled TEXTRELs in the PaX enabled kernel. I'm willing to try without pic though. I'll add to this report in case it changes anything.
 [2009-09-09 03:41 UTC] romain dot riviere at gmail dot com
Just to confirm: the problem is still present without pic.
 [2009-09-09 09:41 UTC] jani@php.net
You also seem to be using --enable-debug, does it happen without it? And can you reproduce the same on command line?
 [2009-09-09 15:28 UTC] romain dot riviere at gmail dot com
The problem still happens without --enable-debug.

I could reproduce the bug in CLI with a simple while(true). More specifically (and with memory_limit=128M this time):

<?php
ini_set('display_errors',true);
if (isset($_GET['size']) && $_GET['size'] < 200) {
$mb = intval($_GET['size']);
}
else {
        $mb=125;
}

while(true) {
$var = '';
echo 'Memory limit: '.ini_get('memory_limit').'<br>';
for ($i=0; $i<=$mb; $i++) {
        $var.= str_repeat('a',1*1024*1024);
        echo memory_get_usage().'<br>';
}
echo 'String length: '. strlen($var);
}
?>

This script crashes after a few iterations with the message:

Fatal error: Out of memory (allocated 123469824) (tried to allocate 122683393 bytes) in /home/acrm/OF-svn/2.0/test.php on line 14
zend_mm_heap corrupted

The memory_get_usage() output increases until it busts the 128M limit.

Now, I'm not too familiar with PHP's memory management, but I find it strange that the script should hog memory that way: shouldn't it free some RAM when $var is reset to '' ?

At any rate, the initial issue remains, whether this one is related or not.
 [2009-09-09 19:42 UTC] jani@php.net
Please try using this snapshot:

  http://snaps.php.net/php5.2-latest.tar.gz
 
For Windows:

  http://windows.php.net/snapshots/

I can not reproduce this at all on several different servers running 
latest svn checkout.
 [2009-09-09 19:43 UTC] jani@php.net
And make sure you really do simply this:

1. Unpack sources
2. ./configure --disable-all --disable-cgi && make
3 run the test script with 'sapi/cli/php -n -d memory_limit=128M 
yourscript.php
 [2009-09-10 06:44 UTC] romain dot riviere at gmail dot com
I was able to reproduce the problem even with the latest sources. I am beginning to suspect PaX/GrSecurity.

Are there any catches to be aware of when running PHP in that context ? I know of the --with-pic/TEXTREL issue, but apparently there must be something else to it.
 [2009-09-12 13:51 UTC] romain dot riviere at gmail dot com
Unfortunately, same difference (Fatal error: Out of memory (allocated 9175040) (tried to allocate 1048577 bytes) in /home/smokey/test.php on line 15).

I need to take some time and experiment a different kernel, with different combinations of VServer/PaX/Grsecurity options, and see if it helps.
 [2009-09-29 17:18 UTC] romain dot riviere at gmail dot com
Just a quick follow-up on this one.

I've finally traced it to PaX's SEGMEXEC feature.
I do not know yet if every PaX kernel should display that behaviour, or if it's just VServer's GrSec patch, but disabling SEGMEXEC fixed it for me.

I guess the bottomline here is : not PHP's fault

Cheers !
 [2009-09-29 18:35 UTC] spender at grsecurity dot net
Due to VMA mirroring, the SEGMEXEC option causes accounted vm usage to double.  So you weren't experiencing a memory leak -- you were just being accounted for twice as much memory as you thought you were using.  The solution would be to double the resource limit or, if your system is NX-capable and PAE is enabled, use PAGEEXEC.

-Brad
 [2009-09-29 19:04 UTC] romain dot riviere at gmail dot com
Brad, thanks for clearing that up. However, I'm not sure I understand how it explains the behaviour I've had: shouldn't the memory allocation give up when reaching about half of the allowed 128M ? It's far from being the case here ...

Without SEGMEXEC: the code (second snip) used to demonstrate the bug runs in an endless loop, as it is supposed to. The string never exceeds PHP's memory limit.

With SEGMEXEC: the code runs a few times, meaning the string is generated up to PHP's memory limit. However, after a few runs, it dies, and it's never even close to half the memory_limit.

What am I missing ?
 [2009-10-06 14:51 UTC] jani@php.net
.
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Fri Apr 18 18:01:58 2014 UTC