Bug #73497 [Com]: memcache session handler with two backend servers Fatal Error (out of memory)

From: Date: Wed, 16 Nov 2016 09:55:40 +0000
Subject: Bug #73497 [Com]: memcache session handler with two backend servers Fatal Error (out of memory)
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-205399@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=73497&edit=1

 ID:                 73497
 Comment by:         php at bof dot de
 Reported by:        php at bof dot de
 Summary:            memcache session handler with two backend servers
                     Fatal Error (out of memory)
 Status:             Open
 Type:               Bug
 Package:            Session related
 Operating System:   openSUSE 11.4 + 13.1, x86_64
 PHP Version:        5.6.28
 Block user comment: N
 Private report:     N

 New Comment:

Re-reported against package "memcache (PECL)" in https://bugs.php.net/bug.php?id=73539


Previous Comments:
------------------------------------------------------------------------
[2016-11-15 15:10:11] php at bof dot de

The following patch fixes the issue for the memcache extension, by making a temporary copy of the
single server parts of save_path before calling php_url_parse_ex:

--- ../release-5.6.28//memcache/memcache_session.c	2016-11-10 16:27:45.963096097 +0100
+++ ./memcache/memcache_session.c	2016-11-15 16:07:09.186618020 +0100
@@ -90,7 +90,10 @@
 				efree(path);
 			}
 			else {
-				url = php_url_parse_ex(save_path+i, j-i);
+				int len = j-i;
+				char *path = estrndup(save_path+i, len);
+				url = php_url_parse_ex(path, strlen(path));
+				efree(path);
 			}
 
 			if (!url) {

------------------------------------------------------------------------
[2016-11-15 14:48:44] php at bof dot de

adding to my previous "analysis".... I was a bit confused because I found that I
couldn't use the PHP level parse_url() function with the same argument as the memcache handler
stuff, to get the same (bad result).

Now, looking closely, the difference is that the memcache extension calls php_url_parse_ex with the
two-server string BUT length SET TO 24, i.e. only covering the first of the two servers in the
string.

But then php_url_parse_ex obviously does not really limit itself to examining the passed-in length,
and happily scans the string further.

------------------------------------------------------------------------
[2016-11-15 14:02:50] php at bof dot de

Reproducing with my test script and an --enable-debug build, gives some hint:

Fatal error: Allowed memory size of 134217728 bytes exhausted at
/usr/src/phb/build/dbg-5.6.28/php-src/ext/standard/url.c:343 (tried to allocate 4294967291 bytes) in
/tmp/badsess.php on line 37

url.c:343 is php_url_parse_ex() line
        ret->path = estrndup(s, (ue-s));

Trying to run under gdb with a breakpoint set there, gives the error in a different place:

Fatal error: Allowed memory size of 134217728 bytes exhausted at
/usr/src/phb/build/dbg-5.6.28/php-src/ext/standard/url.c:339 (tried to allocate 4294965800 bytes) in
/tmp/badsess.php on line 37

That url.c:339 line is:
	ret->fragment = estrndup(p, (ue-p));

Also setting a breakpoint there. Result:

Breakpoint 2, php_url_parse_ex (
    str=0x7ffff7fcc830 "tcp://192.168.8.57:11211, tcp://192.168.8.58:11211", 
    length=24) at /usr/src/phb/build/dbg-5.6.28/php-src/ext/standard/url.c:339
339				ret->fragment = estrndup(p, (ue-p));

The plot thickens... :)

(gdb) print p
$1 = 0x7ffff7fcce21 "\001"
(gdb) print ue
$2 = 0x7ffff7fcc848 ", tcp://192.168.8.58:11211"
(gdb) print ue-p
$3 = -1497

That "p" is "(p = memchr(s, '#', (ue - s)))" from line 329.

(gdb) print s
$4 = 0x7ffff7fcc84e "//192.168.8.58:11211"
(gdb) print ue-s
$5 = -6

So we have a memchr() call already with negative length (probably undefined behaviour).

Here is the change between 5.6.27 and 5.6.28:

--- ../release-5.6.27/php-src/ext/standard/url.c	2016-10-20 17:59:31.906609491 +0200
+++ ./php-src/ext/standard/url.c	2016-11-14 19:01:25.283278279 +0100
@@ -217,28 +217,7 @@
 		goto nohost;
 	}
 
-	e = ue;
-
-	if (!(p = memchr(s, '/', (ue - s)))) {
-		char *query, *fragment;
-
-		query = memchr(s, '?', (ue - s));
-		fragment = memchr(s, '#', (ue - s));
-
-		if (query && fragment) {
-			if (query > fragment) {
-				e = fragment;
-			} else {
-				e = query;
-			}
-		} else if (query) {
-			e = query;
-		} else if (fragment) {
-			e = fragment;
-		}
-	} else {
-		e = p;
-	}
+	e = s + strcspn(s, "/?#");
 
 	/* check for login and password */
 	if ((p = zend_memrchr(s, '@', (e-s)))) {

Given the input string "tcp://192.168.8.57:11211, tcp://192.168.8.58:11211" and
"s" already past the initial "tcp://", the old code would have
found the '/', then wouldn't have found '?' or '#' - AND THUS
WOULD HAVE LEFT "e" alone.

After that change, "e" will be at the first '/' of the SECOND
"argument", so that the code further effectively tries to parse "192.168.8.57:11211,
tcp:" and then goes astray....

------------------------------------------------------------------------
[2016-11-13 13:59:40] angeloxx at angeloxx dot it

Same version of PHP and Memcache plugin and same issue (try to allocate 4GB of memory).

------------------------------------------------------------------------
[2016-11-11 09:16:00] php at bof dot de

Additional information: the memcache module is from https://git.php.net/repository/pecl/caching/memcache.git
checked out with tag memcache-3.0.8 - source files are identical between my 5.6.27 (works) and
5.6.28 (breaks) builds.

------------------------------------------------------------------------


The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at

    https://bugs.php.net/bug.php?id=73497


--
Edit this bug report at https://bugs.php.net/bug.php?id=73497&edit=1


Thread (9 messages)

« previous php.bugs (#205399) next »