Bug 866520

Summary: pcre_exec() segfaults when back-tracking Unicode properties in non-UTF-8 mode
Product: Red Hat Enterprise Linux 5 Reporter: Tomasz Ostrowski <tometzky+redhat>
Component: pcreAssignee: Petr Pisar <ppisar>
Status: CLOSED ERRATA QA Contact: Jan Kepler <jkejda>
Severity: high Docs Contact:
Priority: unspecified    
Version: 5.8CC: jkejda, rcollet
Target Milestone: rcKeywords: Patch
Target Release: ---   
Hardware: Unspecified   
OS: Linux   
Whiteboard:
Fixed In Version: pcre-6.6-9.el5 Doc Type: Bug Fix
Doc Text:
Previously, matching a regular expression with Unicode properties in a non-UTF-8 mode against a string with non-ASCII characters, caused an unexpected termination with a segmentation fault in the PCRE library. This update fixes back-tracking in non-UTF-8 mode, and the PCRE library no longer crashes in the aforementioned scenario.
Story Points: ---
Clone Of: Environment:
Last Closed: 2013-09-30 21:58:44 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 921048    
Attachments:
Description Flags
This file crashes php
none
preg.c
none
API/ABI compatibility report
none
Upstream fix ported to pcre-6.6 none

Description Tomasz Ostrowski 2012-10-15 14:41:05 UTC
Created attachment 627475 [details]
This file crashes php

Description of problem:
php53 crashes on attached file - in preg_replace function

Version-Release number of selected component (if applicable):
php53-5.3.3-13.el5_8
pcre-6.6-6.el5_6.1

How reproducible:
Always

Steps to Reproduce:
1. php preg_replace_crash.php
  
Actual results:
Segmentation fault

Expected results:
<span class="search-everything-highlight-color" style="background-color:#FFF984">foobar</span> &#8211; a &#8211; 00 aaźaaier

Additional info:
This is really on CentOS, but I think you might be interested, as it can be security related. I stumbled on this bug when Wordpress page was crashing httpd.


$ valgrind php preg_replace_crash.php
==26413== Memcheck, a memory error detector
==26413== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==26413== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==26413== Command: php preg_replace_crash.php
==26413== 
==26413== Invalid read of size 1
==26413==    at 0x4A08880: memcpy (mc_replace_strmem.c:587)
==26413==    by 0x4612E2: php_pcre_replace_impl (string3.h:51)
==26413==    by 0x461F9C: php_replace_in_subject (php_pcre.c:1267)
==26413==    by 0x462593: preg_replace_impl (php_pcre.c:1365)
==26413==    by 0x462AC2: zif_preg_replace (php_pcre.c:1385)
==26413==    by 0x5E78E8: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:316)
==26413==    by 0x5BD9AA: execute (zend_vm_execute.h:107)
==26413==    by 0x599F44: zend_execute_scripts (zend.c:1194)
==26413==    by 0x54AD47: php_execute_script (main.c:2261)
==26413==    by 0x622CDD: main (php_cli.c:1192)
==26413==  Address 0x4f7e8bf is 1 bytes before a block of size 262,144 alloc'd
==26413==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==26413==    by 0x57CB2D: _zend_mm_alloc_int (zend_alloc.c:1898)
==26413==    by 0x57D184: zend_mm_shutdown (zend_alloc.c:1657)
==26413==    by 0x5494A6: php_module_startup (main.c:2094)
==26413==    by 0x62189C: php_cli_startup (php_cli.c:401)
==26413==    by 0x6220E4: main (php_cli.c:775)
==26413== 
==26413== Invalid read of size 1
==26413==    at 0x4A08887: memcpy (mc_replace_strmem.c:587)
==26413==    by 0x4612E2: php_pcre_replace_impl (string3.h:51)
==26413==    by 0x461F9C: php_replace_in_subject (php_pcre.c:1267)
==26413==    by 0x462593: preg_replace_impl (php_pcre.c:1365)
==26413==    by 0x462AC2: zif_preg_replace (php_pcre.c:1385)
==26413==    by 0x5E78E8: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:316)
==26413==    by 0x5BD9AA: execute (zend_vm_execute.h:107)
==26413==    by 0x599F44: zend_execute_scripts (zend.c:1194)
==26413==    by 0x54AD47: php_execute_script (main.c:2261)
==26413==    by 0x622CDD: main (php_cli.c:1192)
==26413==  Address 0x4f7e8be is 2 bytes before a block of size 262,144 alloc'd
==26413==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==26413==    by 0x57CB2D: _zend_mm_alloc_int (zend_alloc.c:1898)
==26413==    by 0x57D184: zend_mm_shutdown (zend_alloc.c:1657)
==26413==    by 0x5494A6: php_module_startup (main.c:2094)
==26413==    by 0x62189C: php_cli_startup (php_cli.c:401)
==26413==    by 0x6220E4: main (php_cli.c:775)
==26413== 
==26413== Invalid read of size 1
==26413==    at 0x4A08870: memcpy (mc_replace_strmem.c:587)
==26413==    by 0x4612E2: php_pcre_replace_impl (string3.h:51)
==26413==    by 0x461F9C: php_replace_in_subject (php_pcre.c:1267)
==26413==    by 0x462593: preg_replace_impl (php_pcre.c:1365)
==26413==    by 0x462AC2: zif_preg_replace (php_pcre.c:1385)
==26413==    by 0x5E78E8: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:316)
==26413==    by 0x5BD9AA: execute (zend_vm_execute.h:107)
==26413==    by 0x599F44: zend_execute_scripts (zend.c:1194)
==26413==    by 0x54AD47: php_execute_script (main.c:2261)
==26413==    by 0x622CDD: main (php_cli.c:1192)
==26413==  Address 0x4f7e8bd is 3 bytes before a block of size 262,144 alloc'd
==26413==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==26413==    by 0x57CB2D: _zend_mm_alloc_int (zend_alloc.c:1898)
==26413==    by 0x57D184: zend_mm_shutdown (zend_alloc.c:1657)
==26413==    by 0x5494A6: php_module_startup (main.c:2094)
==26413==    by 0x62189C: php_cli_startup (php_cli.c:401)
==26413==    by 0x6220E4: main (php_cli.c:775)
==26413== 
==26413== Invalid read of size 1
==26413==    at 0x4A08879: memcpy (mc_replace_strmem.c:587)
==26413==    by 0x4612E2: php_pcre_replace_impl (string3.h:51)
==26413==    by 0x461F9C: php_replace_in_subject (php_pcre.c:1267)
==26413==    by 0x462593: preg_replace_impl (php_pcre.c:1365)
==26413==    by 0x462AC2: zif_preg_replace (php_pcre.c:1385)
==26413==    by 0x5E78E8: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:316)
==26413==    by 0x5BD9AA: execute (zend_vm_execute.h:107)
==26413==    by 0x599F44: zend_execute_scripts (zend.c:1194)
==26413==    by 0x54AD47: php_execute_script (main.c:2261)
==26413==    by 0x622CDD: main (php_cli.c:1192)
==26413==  Address 0x4f7e8bc is 4 bytes before a block of size 262,144 alloc'd
==26413==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==26413==    by 0x57CB2D: _zend_mm_alloc_int (zend_alloc.c:1898)
==26413==    by 0x57D184: zend_mm_shutdown (zend_alloc.c:1657)
==26413==    by 0x5494A6: php_module_startup (main.c:2094)
==26413==    by 0x62189C: php_cli_startup (php_cli.c:401)
==26413==    by 0x6220E4: main (php_cli.c:775)
==26413== 
==26413== Invalid write of size 1
==26413==    at 0x4A0888F: memcpy (mc_replace_strmem.c:587)
==26413==    by 0x4612E2: php_pcre_replace_impl (string3.h:51)
==26413==    by 0x461F9C: php_replace_in_subject (php_pcre.c:1267)
==26413==    by 0x462593: preg_replace_impl (php_pcre.c:1365)
==26413==    by 0x462AC2: zif_preg_replace (php_pcre.c:1385)
==26413==    by 0x5E78E8: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:316)
==26413==    by 0x5BD9AA: execute (zend_vm_execute.h:107)
==26413==    by 0x599F44: zend_execute_scripts (zend.c:1194)
==26413==    by 0x54AD47: php_execute_script (main.c:2261)
==26413==    by 0x622CDD: main (php_cli.c:1192)
==26413==  Address 0x4f7e8bf is 1 bytes before a block of size 262,144 alloc'd
==26413==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==26413==    by 0x57CB2D: _zend_mm_alloc_int (zend_alloc.c:1898)
==26413==    by 0x57D184: zend_mm_shutdown (zend_alloc.c:1657)
==26413==    by 0x5494A6: php_module_startup (main.c:2094)
==26413==    by 0x62189C: php_cli_startup (php_cli.c:401)
==26413==    by 0x6220E4: main (php_cli.c:775)
==26413== 
==26413== Invalid write of size 1
==26413==    at 0x4A08877: memcpy (mc_replace_strmem.c:587)
==26413==    by 0x4612E2: php_pcre_replace_impl (string3.h:51)
==26413==    by 0x461F9C: php_replace_in_subject (php_pcre.c:1267)
==26413==    by 0x462593: preg_replace_impl (php_pcre.c:1365)
==26413==    by 0x462AC2: zif_preg_replace (php_pcre.c:1385)
==26413==    by 0x5E78E8: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:316)
==26413==    by 0x5BD9AA: execute (zend_vm_execute.h:107)
==26413==    by 0x599F44: zend_execute_scripts (zend.c:1194)
==26413==    by 0x54AD47: php_execute_script (main.c:2261)
==26413==    by 0x622CDD: main (php_cli.c:1192)
==26413==  Address 0x4f7e8be is 2 bytes before a block of size 262,144 alloc'd
==26413==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==26413==    by 0x57CB2D: _zend_mm_alloc_int (zend_alloc.c:1898)
==26413==    by 0x57D184: zend_mm_shutdown (zend_alloc.c:1657)
==26413==    by 0x5494A6: php_module_startup (main.c:2094)
==26413==    by 0x62189C: php_cli_startup (php_cli.c:401)
==26413==    by 0x6220E4: main (php_cli.c:775)
==26413== 
==26413== Invalid write of size 1
==26413==    at 0x4A0887D: memcpy (mc_replace_strmem.c:587)
==26413==    by 0x4612E2: php_pcre_replace_impl (string3.h:51)
==26413==    by 0x461F9C: php_replace_in_subject (php_pcre.c:1267)
==26413==    by 0x462593: preg_replace_impl (php_pcre.c:1365)
==26413==    by 0x462AC2: zif_preg_replace (php_pcre.c:1385)
==26413==    by 0x5E78E8: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:316)
==26413==    by 0x5BD9AA: execute (zend_vm_execute.h:107)
==26413==    by 0x599F44: zend_execute_scripts (zend.c:1194)
==26413==    by 0x54AD47: php_execute_script (main.c:2261)
==26413==    by 0x622CDD: main (php_cli.c:1192)
==26413==  Address 0x4f7e8bd is 3 bytes before a block of size 262,144 alloc'd
==26413==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==26413==    by 0x57CB2D: _zend_mm_alloc_int (zend_alloc.c:1898)
==26413==    by 0x57D184: zend_mm_shutdown (zend_alloc.c:1657)
==26413==    by 0x5494A6: php_module_startup (main.c:2094)
==26413==    by 0x62189C: php_cli_startup (php_cli.c:401)
==26413==    by 0x6220E4: main (php_cli.c:775)
==26413== 
==26413== Invalid write of size 1
==26413==    at 0x4A08884: memcpy (mc_replace_strmem.c:587)
==26413==    by 0x4612E2: php_pcre_replace_impl (string3.h:51)
==26413==    by 0x461F9C: php_replace_in_subject (php_pcre.c:1267)
==26413==    by 0x462593: preg_replace_impl (php_pcre.c:1365)
==26413==    by 0x462AC2: zif_preg_replace (php_pcre.c:1385)
==26413==    by 0x5E78E8: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:316)
==26413==    by 0x5BD9AA: execute (zend_vm_execute.h:107)
==26413==    by 0x599F44: zend_execute_scripts (zend.c:1194)
==26413==    by 0x54AD47: php_execute_script (main.c:2261)
==26413==    by 0x622CDD: main (php_cli.c:1192)
==26413==  Address 0x4f7e8bc is 4 bytes before a block of size 262,144 alloc'd
==26413==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==26413==    by 0x57CB2D: _zend_mm_alloc_int (zend_alloc.c:1898)
==26413==    by 0x57D184: zend_mm_shutdown (zend_alloc.c:1657)
==26413==    by 0x5494A6: php_module_startup (main.c:2094)
==26413==    by 0x62189C: php_cli_startup (php_cli.c:401)
==26413==    by 0x6220E4: main (php_cli.c:775)
==26413== 
==26413== 
==26413== Process terminating with default action of signal 11 (SIGSEGV)
==26413==  Access not within mapped region at address 0x4C13FFF
==26413==    at 0x4A08880: memcpy (mc_replace_strmem.c:587)
==26413==    by 0x4612E2: php_pcre_replace_impl (string3.h:51)
==26413==    by 0x461F9C: php_replace_in_subject (php_pcre.c:1267)
==26413==    by 0x462593: preg_replace_impl (php_pcre.c:1365)
==26413==    by 0x462AC2: zif_preg_replace (php_pcre.c:1385)
==26413==    by 0x5E78E8: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:316)
==26413==    by 0x5BD9AA: execute (zend_vm_execute.h:107)
==26413==    by 0x599F44: zend_execute_scripts (zend.c:1194)
==26413==    by 0x54AD47: php_execute_script (main.c:2261)
==26413==    by 0x622CDD: main (php_cli.c:1192)
==26413==  If you believe this happened as a result of a stack
==26413==  overflow in your program's main thread (unlikely but
==26413==  possible), you can try to increase the size of the
==26413==  main thread stack using the --main-stacksize= flag.
==26413==  The main thread stack size used in this run was 10485760.
==26413== Invalid read of size 8
==26413==    at 0x396EA08E15: do_lookup_x (do-lookup.h:47)
==26413==    by 0x396EA092B1: _dl_lookup_symbol_x (dl-lookup.c:280)
==26413==    by 0x396EA0CF64: _dl_fixup (dl-runtime.c:108)
==26413==    by 0x396EA129E1: _dl_runtime_resolve (in /lib64/ld-2.5.so)
==26413==    by 0x48024E8: _vgnU_freeres (vg_preloaded.c:62)
==26413==    by 0x4F9777E: ???
==26413==    by 0x4612E2: php_pcre_replace_impl (string3.h:51)
==26413==    by 0x461F9C: php_replace_in_subject (php_pcre.c:1267)
==26413==    by 0x462593: preg_replace_impl (php_pcre.c:1365)
==26413==    by 0x462AC2: zif_preg_replace (php_pcre.c:1385)
==26413==    by 0x5E78E8: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:316)
==26413==    by 0x5BD9AA: execute (zend_vm_execute.h:107)
==26413==  Address 0x28 is not stack'd, malloc'd or (recently) free'd
==26413== 
==26413== 
==26413== Process terminating with default action of signal 11 (SIGSEGV)
==26413==  Access not within mapped region at address 0x28
==26413==    at 0x396EA08E15: do_lookup_x (do-lookup.h:47)
==26413==    by 0x396EA092B1: _dl_lookup_symbol_x (dl-lookup.c:280)
==26413==    by 0x396EA0CF64: _dl_fixup (dl-runtime.c:108)
==26413==    by 0x396EA129E1: _dl_runtime_resolve (in /lib64/ld-2.5.so)
==26413==    by 0x48024E8: _vgnU_freeres (vg_preloaded.c:62)
==26413==    by 0x4F9777E: ???
==26413==    by 0x4612E2: php_pcre_replace_impl (string3.h:51)
==26413==    by 0x461F9C: php_replace_in_subject (php_pcre.c:1267)
==26413==    by 0x462593: preg_replace_impl (php_pcre.c:1365)
==26413==    by 0x462AC2: zif_preg_replace (php_pcre.c:1385)
==26413==    by 0x5E78E8: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:316)
==26413==    by 0x5BD9AA: execute (zend_vm_execute.h:107)
==26413==  If you believe this happened as a result of a stack
==26413==  overflow in your program's main thread (unlikely but
==26413==  possible), you can try to increase the size of the
==26413==  main thread stack using the --main-stacksize= flag.
==26413==  The main thread stack size used in this run was 10485760.
==26413== 
==26413== HEAP SUMMARY:
==26413==     in use at exit: 2,714,420 bytes in 16,247 blocks
==26413==   total heap usage: 17,374 allocs, 1,127 frees, 3,110,948 bytes allocated
==26413== 
==26413== LEAK SUMMARY:
==26413==    definitely lost: 1,497,751 bytes in 15,954 blocks
==26413==    indirectly lost: 599 bytes in 11 blocks
==26413==      possibly lost: 786,473 bytes in 4 blocks
==26413==    still reachable: 429,597 bytes in 278 blocks
==26413==         suppressed: 0 bytes in 0 blocks
==26413== Rerun with --leak-check=full to see details of leaked memory
==26413== 
==26413== For counts of detected and suppressed errors, rerun with: -v
==26413== ERROR SUMMARY: 3238500 errors from 9 contexts (suppressed: 78 from 7)
Segmentation fault

Comment 1 Remi Collet 2012-10-17 13:57:31 UTC
Crash reproduced on rhel5 with php53 5.3.3 and pcre 6.6.6
Not reproduced on rhel6 with php 5.3.3 and pcre 7.8

After investigation on this issue, It seems to be a "pcre" issue.

In some case, pcre_exec returns a count value > 0 when it should be -1 (PCRE_ERROR_NOMATCH), and the offset vector contains dummy data.

Comment 2 Remi Collet 2012-10-17 14:00:59 UTC
Created attachment 628829 [details]
preg.c

Expected output:

start:0, count:2
offsets[0]=0
offsets[1]=6
offsets[2]=0
offsets[3]=6
start:6, count:-1

Actual output: 

start:0, count:2
offsets[0]=0
offsets[1]=6
offsets[2]=0
offsets[3]=6
start:6, count:2
offsets[0]=31
offsets[1]=6
offsets[2]=31
offsets[3]=6

start offset should never be > than end

Comment 3 Petr Pisar 2012-10-17 15:55:13 UTC
It's a bug in PCRE. The reproducer can be rewritten for pcretest this way:

$ printf '%s\n%s\n' \
  '/(?<!\<)(?<!\w)(\pL*foobar\pL*)(?!\w|[^<>]*>)/' \
  ' &#8211; a &#8211; 00 aaźaaier' | pcretest
PCRE version 6.6 06-Feb-2006

  re> Segmentation fault

Comment 4 Petr Pisar 2012-10-17 16:07:34 UTC
Minimal test case:

$ printf '%s\n%s\n' '/\pL*a\pL/' 'źa' | pcretest
PCRE version 6.6 06-Feb-2006

  re> Segmentation fault

There seems to be problem when using Unicode properties in ASCII mode on string with some non-ASCII characters.

Comment 5 Tomasz Ostrowski 2012-10-18 09:55:34 UTC
I've bisected fix for this bug using minimal test case to revision 207 in pcre svn:
http://vcs.pcre.org/viewvc?view=revision&revision=207

But a diff for this revision does not apply cleanly to revision 6.6. I tried to port it, but failed - it was still crashing. The fix has to be dependent on earlier code changes.

Comment 6 Tomasz Ostrowski 2012-10-18 10:58:20 UTC
Created attachment 629315 [details]
API/ABI compatibility report

Maybe a simplest solution would be to just upgrade pcre to version 7.3 - the same which RHEL6 uses.

I've compared this 2 version of library using ABI Compliance Checker 1.98.4 (http://ispras.linuxbase.org/index.php/ABI_compliance_checker) and I'm attaching a compatibility report. It looks like these 2 versions are compatible, so a new version should just work.

Comment 7 Tomasz Ostrowski 2012-10-18 14:22:10 UTC
(In reply to comment #6)
> Maybe a simplest solution would be to just upgrade pcre to version 7.3 - the
> same which RHEL6 uses.

Correction - RHEL6 uses 7.8, which is binary compatible with 6.6, but isn't source compatible - it lacks a global no_arg variable in namespace pcrecpp. Latest pcre version which is binary and source compatible with 6.6 is 7.5.

Comment 8 Petr Pisar 2012-10-19 12:27:58 UTC
Thank you for finding the fix. Actually, it was quite easy to back-port it.

Comment 9 Petr Pisar 2012-10-19 12:28:42 UTC
Created attachment 630005 [details]
Upstream fix ported to pcre-6.6

Comment 10 RHEL Program Management 2012-10-19 12:29:11 UTC
This request was evaluated by Red Hat Product Management for
inclusion in the current release of Red Hat Enterprise Linux.
Because the affected component is not scheduled to be updated
in the current release, Red Hat is unable to address this
request at this time.

Red Hat invites you to ask your support representative to
propose this request, if appropriate, in the next release of
Red Hat Enterprise Linux.

Comment 13 RHEL Program Management 2013-04-04 12:32:39 UTC
This request was evaluated by Red Hat Product Management for inclusion
in a Red Hat Enterprise Linux release.  Product Management has
requested further review of this request by Red Hat Engineering, for
potential inclusion in a Red Hat Enterprise Linux release for currently
deployed products.  This request is not yet committed for inclusion in
a release.

Comment 20 errata-xmlrpc 2013-09-30 21:58:44 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory, and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

http://rhn.redhat.com/errata/RHBA-2013-1298.html