Bug #79027 [Com]: Heap corruption in accel_remap_huge_pages
| From: | chris at neadwerx dot com | Date: | Tue, 31 Dec 2019 13:36:34 +0000 |
| Subject: | Bug #79027 [Com]: Heap corruption in accel_remap_huge_pages | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-224637@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=79027&edit=1
ID: 79027
Comment by: chris at neadwerx dot com
Reported by: chris at neadwerx dot com
Summary: Heap corruption in accel_remap_huge_pages
Status: Open
Type: Bug
Package: opcache
Operating System: Linux
PHP Version: Irrelevant
Block user comment: N
Private report: N
New Comment:
Two patches are submitted, The first uses the MAP_HUGE_2MB flag to control mmap() behavior, but this
macro may not be defined on all systems, and may need macro support to work properly.
The second patch checks /proc/meminfo to verify the system's default hugepage size, and
validates that the allocation size is not smaller than the hugepage size (the 1GiB case) prior to
calling accel_remap_huge_pages. If this is not the case, emit an actionable error to the user. Macro
definitions for MAP_HUGE_2MB and MAP_HUGE_1GB can be found in mmap(2)
Previous Comments:
------------------------------------------------------------------------
[2019-12-31 13:32:23] chris at neadwerx dot com
The following patch has been added/updated:
Patch Name: accel_get_hugepage_size
Revision: 1577799143
URL: https://bugs.php.net/patch-display.php?bug=79027&patch=accel_get_hugepage_size&revision=1577799143
------------------------------------------------------------------------
[2019-12-31 12:37:51] chris at neadwerx dot com
The following patch has been added/updated:
Patch Name: PHP7_huge_2mb_flag.patch
Revision: 1577795871
URL: https://bugs.php.net/patch-display.php?bug=79027&patch=PHP7_huge_2mb_flag.patch&revision=1577795871
------------------------------------------------------------------------
[2019-12-24 14:18:07] chris at neadwerx dot com
Description:
------------
Prerequisites:
- System has CPU flag PDPE1GB (basically sandy bridge era or better)
- System configured with hugepage size of 1GB
- PHP version > 7.0
- /etc/php.d/10-opcache.ini:opcache.huge_code_pages=1
Execution:
PHP has a 0.2% chance that execve() loads PHP in the upper 512 4 KiB pages of a 1 GiB page. This
causes zend_move_code_to_huge_pages to select start address for the mmap allocation to be 1 GiB
aligned (ZEND_MM_ALIGNED_SIZE_EX for the start address - both 2MiB and 1GiB alignment will be the
same).
From mmap(2). This will result in a successful allocation of a full 1 GiB, resulting in a corrupted
heap. PHP will receive a SIGSEGV when returning from zend_remap_huge_pages and calling fclose on the
file pointer in zend_move_code_to_huge_pages()
Remediation:
- Modify zend_remap_huge_pages to call mmap with the MAP_HUGE_2MB flag
or
- Check system default hugepage size by opening /proc/meminfo and reading in Hugepagesize setting -
this isn't very portable but ensures that the correct page size can be used, or the function
can exit gracefully.
Test script:
---------------
#!/usr/bin/perl -w
while(1)
{
system( 'php /some/php/file' ); # doesn't need to be a CLI call, can be from
apache
}
Expected result:
----------------
Nothing
Actual result:
--------------
0.2% chance of SIGSEGV
It was very difficult to get a useful trace for this, This is the best I could come up with as GDB
was being very uncooperative:
#0: 0x7f898aa80ff4 _IO_new_fclose /usr/lib64/libc-2.17.so (offset 0x6dff4)
#1: 0x562b420466c0 ??
#2: 0x200000 ??
#3: 0x7f8963303000 ??
#4: 0x7f8982e617f7 accel_move_code_to_huge_pages /usr/lib64/php/modules/opcache.so (offset 0x107f7)
#5: 0x562b3ff78000 ??
#6: 0x562b403dc000 /usr/bin/php (offset 0x464000)
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=79027&edit=1