php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #73933 error/segfault with ldap_mod_replace and opcache
Submitted: 2017-01-14 11:37 UTC Modified: 2017-01-20 04:04 UTC
From: ryan dot brothers at gmail dot com Assigned: laruence
Status: Closed Package: opcache
PHP Version: 7.1.0 OS: Linux
Private report: No CVE-ID:
 [2017-01-14 11:37 UTC] ryan dot brothers at gmail dot com
Description:
------------
I am running into an intermittent issue when using ldap_mod_replace with opcache enabled over php-fpm.  Sometimes the script succeeds and other times, the same script fails or has a segfault.

It's tricky to simulate, but I believe the below script shows the issue.  The script returns the error "Can't contact LDAP server" correctly on the first run, but later runs sometime return the error "Encoding error" or a segfault.  I was expecting the error message to be the same on each run.

The issue only occurs with opcache enabled.  When opcache is disabled, the error message is the same on each run.

The issue also only happens over php-fpm.  When I run the script over CLI, the error message is the same on each run.

I am running PHP 7.1 on CentOS 7, but the problem appears to have started in PHP 7 as PHP 5.6.29 seems ok.


Test script:
---------------
<?php
define('AAA', 1);

error_reporting(E_ALL);

$ldap = ldap_connect('127.0.0.1', 5000);

ldap_mod_replace($ldap, null, array(
	'lockoutTime' => array(0),
));

ldap_close($ldap);


Expected result:
----------------
Warning: ldap_mod_replace(): Modify: Can't contact LDAP server in /var/www/html/ldap.php on line 9


Actual result:
--------------
Sometimes:
Warning: ldap_mod_replace(): Modify: Can't contact LDAP server in /var/www/html/ldap.php on line 9

Other times:
Warning: ldap_mod_replace(): Modify: Encoding error in /var/www/html/ldap.php on line 9

or a segfault


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-01-14 12:39 UTC] nikic@php.net
Can you do a run with -d opcache.protect_memory=1 and see if there's a consistent bus error? From a quick look, php_ldap_do_modify() is doing in-place string conversions on array elements, which may lead to SHM corruption of immutable arrays. If this is indeed the problem, this should be easy to fix by someone with a working ldap setup by replace convert_to_string_ex with zval_get_string.
 [2017-01-14 13:05 UTC] ryan dot brothers at gmail dot com
Thanks.  Yes, if I do opcache.protect_memory=1, then the segfault happens every time when running over CLI.  You should be able to reproduce without a working LDAP server.  In my script above, 127.0.0.1:5000 is just a made-up port with nothing listening on it.  I'm running this to cause the segfault:

php -n -d zend_extension=opcache.so -d opcache.enable_cli=1 -d opcache.protect_memory=1 ldap.php

where ldap.php is my test script above.
 [2017-01-16 03:37 UTC] laruence@php.net
I think it is because ldap edit the const array argument in place, please try with the following patch:

diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c
index 4068384..5c1b6da 100644
--- a/ext/ldap/ldap.c
+++ b/ext/ldap/ldap.c
@@ -1427,7 +1427,7 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper)
        zend_ulong index;
        int is_full_add=0; /* flag for full add operation so ldap_mod_add can be put back into oper, gerrit THomson */

-   if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsa", &link, &dn, &dn_len, &entry) != SUCCESS) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsa/", &link, &dn, &dn_len, &entry) != SUCCESS) {
                return;
        }


thanks
 [2017-01-16 03:45 UTC] laruence@php.net
@nikic didn't notice your comment, it can not be done by changing convert to zval_get_string, since it require an place to hold the zend_string(ldap using char * directly).

so, separate array is a better way to fix this.
 [2017-01-16 12:32 UTC] ryan dot brothers at gmail dot com
laruence - thanks, I'm still seeing the segfault or various errors such as "Invalid syntax" or "Encoding error" when repeatedly calling the script with your patch.  It always works the first time, but not later runs.  Using opcache.protect_memory, you should be able to reproduce the segfault in CLI without having a working LDAP server:

php -n -d zend_extension=opcache.so -d opcache.enable_cli=1 -d opcache.protect_memory=1 ldap.php

<?php
define('AAA', 1);

error_reporting(E_ALL);

$ldap = ldap_connect('127.0.0.1', 5000);

ldap_mod_replace($ldap, null, array(
	'lockoutTime' => array(0),
));

ldap_close($ldap);
 [2017-01-16 13:07 UTC] nikic@php.net
@laruence: This only separates the outer array. The inner array(s) also need to be separated.

You're right that using zval_get_string() here is inconvenient, because we'd have to release the strings later.
 [2017-01-17 07:35 UTC] laruence@php.net
Automatic comment on behalf of laruence@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=b28c2e20ca83ffb5dc9159d7d03f0baf55b0aeaf
Log: Fixed bug #73933 (error/segfault with ldap_mod_replace and opcache)
 [2017-01-17 07:35 UTC] laruence@php.net
-Status: Open +Status: Closed
 [2017-01-17 12:06 UTC] ryan dot brothers at gmail dot com
-Status: Closed +Status: Assigned
 [2017-01-17 12:06 UTC] ryan dot brothers at gmail dot com
laruence - Thank you for the quick fix.  Could you please also check the ldap_modify_batch function?  I think it's the same problem there with the segfault.  A reproduce script is:

<?php
$ldap = ldap_connect('127.0.0.1', 5000);

ldap_modify_batch($ldap, null, array());

ldap_close($ldap);
 [2017-01-20 04:04 UTC] laruence@php.net
-Status: Assigned +Status: Closed -Assigned To: +Assigned To: laruence
 [2017-01-20 04:04 UTC] laruence@php.net
it should be fixed
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Tue Aug 29 15:01:52 2017 UTC