php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #80533 xml extension leaks pthread keys on Apache reload
Submitted: 2020-12-19 22:16 UTC Modified: 2021-05-12 06:29 UTC
From: pp at siedziba dot pl Assigned:
Status: Not a bug Package: *XML functions
PHP Version: 7.4 OS: Linux
Private report: No CVE-ID: None
View Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
If you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: pp at siedziba dot pl
New email:
PHP Version: OS:

 

 [2020-12-19 22:16 UTC] pp at siedziba dot pl
Description:
------------
Every time Apache httpd is gracefully restarted, PHP xml module leaks one xmlFreeGlobalState pthread key.

When the number of keys reaches PTHREAD_KEYS_MAX and no more can be allocated, it still seems to work, but may cause malfunctions and crashes in other code.

One such case is curl extension, using libcurl compiled with NSS. If NSS can't allocate a new pthread key, it crashes Apache process with an assertion failure like this:

"Assertion failure: 0 == rv, at ptthread.c:970"

All Apache processes go away and it needs to be started again.

Older PHP versions are also affected. I noticed the problem with Apache dying during log rotation while still using an older 7.3 release from Sury repo on CentOS 7. I started debugging the problem after upgrading to latest 7.3, 7.4 and 8.0 didn't help.

I created a Dockerfile replicating the problem (URL in "Test script" field). It builds PHP 8.0.0 with latest releases of Apache httpd, libxml2 and curl on Debian 10, then proceeds to reload Apache in a loop until it dies, displaying contents of __pthread_keys array on each iteration.

It starts with two xmlFreeGlobalState keys after first reload:

$1 = {{seq = 1, destr = 0x7fb9a8b88640 <free_key_mem>}, {seq = 3, destr = 0x0}, {seq = 1, destr = 0x7fb9a7987e10 <xmlFreeGlobalState>}, {seq = 3, destr = 0x7fb9a7987e10 <xmlFreeGlobalState>}, {seq = 1, destr = 0x7fb9a76b90f0}, {seq = 0, destr = 0x0} <repeats 1019 times>}

...and ends with:

$1 = {{seq = 1, destr = 0x7fb9a8b88640 <free_key_mem>}, {seq = 2041, destr = 0x0}, {seq = 1, destr = 0x7fb9a7987e10 <xmlFreeGlobalState>}, {seq = 3, destr = 0x7fb9a7987e10 <xmlFreeGlobalState>} <repeats 1020 times>, {seq = 1, destr = 0x7fb9a76b90f0}}
[Inferior 1 (process 10) detached]
Unable to attach: program terminated with signal SIGABRT, Aborted.
No symbol table is loaded.  Use the "file" command.
/bin/sh: 1: kill: No such process


Test script:
---------------
https://gist.github.com/rot13/92108533811a8ab15b5e685f24449b36

Expected result:
----------------
The number of xmlFreeGlobalState keys in __pthread_keys does not increase. Apache keeps working after 1022 reloads.

Actual result:
--------------
The number of xmlFreeGlobalState keys in __pthread_keys increases and Apache dies.

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-12-22 16:01 UTC] cmb@php.net
-PHP Version: 8.0.0 +PHP Version: 7.4
 [2021-05-12 06:29 UTC] krakjoe@php.net
-Status: Open +Status: Not a bug
 [2021-05-12 06:29 UTC] krakjoe@php.net
Sorry, but your problem does not imply a bug in PHP itself.  For a
list of more appropriate places to ask for help using PHP, please
visit http://www.php.net/support.php as this bug system is not the
appropriate forum for asking support questions.  Due to the volume
of reports we can not explain in detail here why your report is not
a bug.  The support channels will be able to provide an explanation
for you.

Thank you for your interest in PHP.

I don't think there's a reasonable way to fix this, we are doing as the libxml authors advise.

> WARNING: if your application is multithreaded or has plugin support calling this may crash the application if another thread or a plugin is still using libxml2. It's sometimes very hard to guess if libxml2 is in use in the application, some libraries or plugins may use it without notice. In case of doubt abstain from calling this function or do it just before calling exit() to avoid leak reports from valgrind !
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Mon Dec 30 14:01:28 2024 UTC