Skip to content

intl causing segfault in docker images #11874

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
adrianrudnik opened this issue Aug 4, 2023 · 3 comments
Closed

intl causing segfault in docker images #11874

adrianrudnik opened this issue Aug 4, 2023 · 3 comments

Comments

@adrianrudnik
Copy link

Description

I was not able to reproduce it with a simple code or any CLI execution, it only works when it is called through FPM or Apache docker images. I've made an example repository for reproduction that uses the official php:8.2.8-apache tag to produce the segfault.

https://github.com/adrianrudnik/intl-segfault

It uses a simplified index.php, Dockerfile and composer for symfony/intl that got me the segfault.

How to reproduce:

git clone https://github.com/adrianrudnik/intl-segfault
cd intl-segfault
docker build -t intl-segfault .
docker run --rm -it -p 8080:80 intl-segfault

then open http://localhost:8080/ to trigger the following docker log entries:

AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
[Fri Aug 04 14:21:41.749364 2023] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.57 (Debian) PHP/8.2.8 configured -- resuming normal operations
[Fri Aug 04 14:21:41.749378 2023] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'
[Fri Aug 04 14:21:49.783205 2023] [core:notice] [pid 1] AH00052: child pid 17 exit signal Segmentation fault (11)

GDB and coredumps are configured and should be debuggable on the containers shell with (and docker exec):

cd /tmp
gdb /usr/sbin/apache2 /tmp/coredump-apache*

Output on my machine:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00007ff23043baad in _efree () from /usr/lib/apache2/modules/libphp.so
(gdb) bt
#0  0x00007ff23043baad in _efree () from /usr/lib/apache2/modules/libphp.so
#1  0x00007ff2314bb8d9 in intl_error_reset () from /usr/local/lib/php/extensions/no-debug-non-zts-20220829/intl.so
#2  0x00007ff2314f079e in ?? () from /usr/local/lib/php/extensions/no-debug-non-zts-20220829/intl.so
#3  0x00007ff2304bb7ed in zend_fe_fetch_object_helper_SPEC () from /usr/lib/apache2/modules/libphp.so
#4  0x00007ff2304d1fe4 in execute_ex () from /usr/lib/apache2/modules/libphp.so
#5  0x00007ff2304d54d3 in zend_execute () from /usr/lib/apache2/modules/libphp.so
#6  0x00007ff230465a08 in zend_execute_scripts () from /usr/lib/apache2/modules/libphp.so
#7  0x00007ff2304009fe in php_execute_script () from /usr/lib/apache2/modules/libphp.so
#8  0x00007ff230549388 in php_handler () from /usr/lib/apache2/modules/libphp.so
#9  0x0000559729934bf0 in ap_run_handler ()
#10 0x00005597299351d6 in ap_invoke_handler ()
#11 0x000055972994d747 in ap_process_async_request ()
#12 0x000055972994d94f in ap_process_request ()
#13 0x0000559729949a14 in ?? ()
#14 0x000055972993e8a0 in ap_run_process_connection ()
#15 0x00007ff232693ca4 in ?? () from /usr/lib/apache2/modules/mod_mpm_prefork.so
#16 0x00007ff232694027 in ?? () from /usr/lib/apache2/modules/mod_mpm_prefork.so
#17 0x00007ff232694089 in ?? () from /usr/lib/apache2/modules/mod_mpm_prefork.so
#18 0x00007ff2326947b3 in ?? () from /usr/lib/apache2/modules/mod_mpm_prefork.so
#19 0x0000559729915640 in ap_run_mpm ()
#20 0x000055972990d16a in main ()

PHP Version

PHP 8.2.8

Operating System

Docker version 24.0.5, build ced0996. Pop!_OS 22.04 LTS

@adrianrudnik
Copy link
Author

As a workaround I had to convert the line

foreach(\IntlTimeZone::createTimeZoneIDEnumeration(\IntlTimeZone::TYPE_ANY) as $id) {

to

foreach(iterator_to_array(\IntlTimeZone::createTimeZoneIDEnumeration(\IntlTimeZone::TYPE_ANY)) as $id) {

Maybe this gives a hint to where the bug lies.

@nielsdos
Copy link
Member

nielsdos commented Aug 4, 2023

The absolute minimal reproducer is one of these two, they both crash under ASAN:

foreach(IntlCalendar::getKeywordValuesForLocale('calendar', 'fa_IR', true) as $id) {
	debug_zval_dump($id);
}
foreach(IntlTimeZone::createTimeZoneIDEnumeration(IntlTimeZone::TYPE_ANY) as $id) {
	debug_zval_dump($id);
}

It seems to crash because the refcount of an object drops to 0 while it's still referenced from somewhere.
This causes a UAF.
Doesn't happen if you store the objects to a variable instead.

nielsdos added a commit to nielsdos/php-src that referenced this issue Aug 13, 2023
The segfault happens because zoi->wrapping_obj points to an object that
has been freed. This wrapping_obj is set in
IntlIterator_from_StringEnumeration(). Notice how the refcount is not
increased in this function. By switching to ZVAL_OBJ_COPY, the segfault
disappears. However, now we get memleaks.

The reason we get a memleak is because now we're responsible for
destroying the reference copy we made with ZVAL_OBJ_COPY.
It would appear this would already be done in zoi_with_current_dtor.
This did work in the past for CVs because the live variable cleanup when
the CV gets destroyed would set the object to UNDEF.

To add more confusion:
The zoi_with_current_dtor() comments seem to indicate that it can be
called twice. However, that seems to be _not_ the case as it's the dtor
for zend_object_iterator_funcs.

This patch reworks how the destruction works in addition to the
ZVAL_OBJ_COPY change.

I checked other of wrapping_obj. The only other place is in
BreakIterator. BreakIterator doesn't actually use the wrapping_obj
for anything however, and doesn't manage that refcount,
so set it to UNDEF to be safe.
@nielsdos nielsdos linked a pull request Aug 13, 2023 that will close this issue
@nielsdos
Copy link
Member

I can't figure it out, I made some progress in my PR but I'm stuck. I closed my PR, someone else feel free to work on this.

nielsdos added a commit to nielsdos/php-src that referenced this issue Jan 3, 2025
The segfault happens because zoi->wrapping_obj points to an object that has been freed.
This wrapping_obj is set in IntlIterator_from_StringEnumeration().
Notice how the refcount is not increased in this function.
By switching to ZVAL_OBJ_COPY, the segfault disappears.

We also need to move the responsibility of destroying the iterator to
the iterator itself and keep the object data destruction in the object
destruction. The existing code used a weird recursive destruction
between the iterator and object that was too hard to understand to be
honest. This patch simplifies everything and in the process gets rid of
the leak.

Iterators that are embedded are now responsible for their own
memory cleanup.
@nielsdos nielsdos linked a pull request Jan 3, 2025 that will close this issue
nielsdos added a commit that referenced this issue Jan 3, 2025
* PHP-8.3:
  Fix GH-11874: intl causing segfault in docker images
nielsdos added a commit that referenced this issue Jan 3, 2025
* PHP-8.4:
  Fix GH-11874: intl causing segfault in docker images
charmitro pushed a commit to wasix-org/php that referenced this issue Mar 13, 2025
The segfault happens because zoi->wrapping_obj points to an object that has been freed.
This wrapping_obj is set in IntlIterator_from_StringEnumeration().
Notice how the refcount is not increased in this function.
By switching to ZVAL_OBJ_COPY, the segfault disappears.

We also need to move the responsibility of destroying the iterator to
the iterator itself and keep the object data destruction in the object
destruction. The existing code used a weird recursive destruction
between the iterator and object that was too hard to understand to be
honest. This patch simplifies everything and in the process gets rid of
the leak.

Iterators that are embedded are now responsible for their own
memory cleanup.

Closes phpGH-17343.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants