Skip to content

heap-buffer-overflow in PHP preg_replace on Mac M1 in Linux #302

@morrisonlevi

Description

@morrisonlevi

I have struggled to create a simple reproducer for this, but I do have a Dockerfile below. Here is the sanitizer output:

=================================================================
==7558==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60d00011e40f at pc 0xaaaac6c6aaa8 bp 0xffffc13ae370 sp 0xffffc13ae368
READ of size 16 at 0x60d00011e40f thread T0
=================================================================
==7557==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60600007a87f at pc 0xaaaac5c5aaa8 bp 0xfffff3aea780 sp 0xfffff3aea778
READ of size 16 at 0x60600007a87f thread T0
    #0 0xaaaac6c6aaa4 in ffcps_0 /usr/local/src/php/ext/pcre/pcre2lib/pcre2_jit_neon_inc.h:267:22
    #1 0xffffa9914284  (<unknown module>)
    #2 0xaaaac6c28ffc in php_pcre2_jit_match /usr/local/src/php/ext/pcre/pcre2lib/pcre2_jit_match.c:165:8
    #3 0xaaaac6d25bac in php_pcre_replace_impl /usr/local/src/php/ext/pcre/php_pcre.c:1629:11
    #4 0xaaaac6d2741c in php_pcre_replace /usr/local/src/php/ext/pcre/php_pcre.c:1571:11
    #5 0xaaaac6d2741c in php_replace_in_subject /usr/local/src/php/ext/pcre/php_pcre.c:2141:12
    #6 0xaaaac6d2741c in preg_replace_common /usr/local/src/php/ext/pcre/php_pcre.c:2279:12
    #7 0xaaaac7820838 in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER /usr/local/src/php/Zend/zend_vm_execute.h:1337:2
    #8 0xaaaac772b0cc in execute_ex /usr/local/src/php/Zend/zend_vm_execute.h:56987:7
    #9 0xaaaac772b7ac in zend_execute /usr/local/src/php/Zend/zend_vm_execute.h:61584:2
    #10 0xaaaac76adcbc in zend_execute_scripts /usr/local/src/php/Zend/zend.c:1876:4
    #11 0xaaaac757ef14 in php_execute_script /usr/local/src/php/main/main.c:2492:13
    #12 0xaaaac7a0b730 in do_cli /usr/local/src/php/sapi/cli/php_cli.c:966:5
    #13 0xaaaac7a09080 in main /usr/local/src/php/sapi/cli/php_cli.c:1340:18
    #14 0xffffaacc777c  (/usr/lib/aarch64-linux-gnu/libc.so.6+0x2777c) (BuildId: c051f80d4fd82e94cd3e72f0eac71ed4f5c0ccbf)
    #15 0xffffaacc7854 in __libc_start_main (/usr/lib/aarch64-linux-gnu/libc.so.6+0x27854) (BuildId: c051f80d4fd82e94cd3e72f0eac71ed4f5c0ccbf)
    #16 0xaaaac6a2986c in _start (/opt/php/nts-asan/bin/php+0x40986c) (BuildId: aced2656c333be293f1ad721ee2fb70efbd41d0b)

0x60d00011e40f is located 7 bytes after 136-byte region [0x60d00011e380,0x60d00011e408)
allocated by thread T0 here:
    #0 0xaaaac6ac84d8 in __interceptor_realloc (/opt/php/nts-asan/bin/php+0x4a84d8) (BuildId: aced2656c333be293f1ad721ee2fb70efbd41d0b)
    #1 0xaaaac761b8a0 in __zend_realloc /usr/local/src/php/Zend/zend_alloc.c:3149:6
    #2 0xaaaac761b8a0 in tracked_realloc /usr/local/src/php/Zend/zend_alloc.c:2883:8
    #3 0xaaaac7695f0c in zend_string_extend /usr/local/src/php/Zend/zend_string.h:271:25
    #4 0xaaaac7695f0c in concat_function /usr/local/src/php/Zend/zend_operators.c:2070:17
    #5 0xaaaac778bf88 in zend_binary_op /usr/local/src/php/Zend/zend_execute.c:1560:9
    #6 0xaaaac778bf88 in ZEND_ASSIGN_DIM_OP_SPEC_VAR_CV_HANDLER /usr/local/src/php/Zend/zend_vm_execute.h:30194:4
    #7 0xaaaac772b0cc in execute_ex /usr/local/src/php/Zend/zend_vm_execute.h:56987:7
    #8 0xaaaac772b7ac in zend_execute /usr/local/src/php/Zend/zend_vm_execute.h:61584:2
    #9 0xaaaac76adcbc in zend_execute_scripts /usr/local/src/php/Zend/zend.c:1876:4
    #10 0xaaaac757ef14 in php_execute_script /usr/local/src/php/main/main.c:2492:13
    #11 0xaaaac7a0b730 in do_cli /usr/local/src/php/sapi/cli/php_cli.c:966:5
    #12 0xaaaac7a09080 in main /usr/local/src/php/sapi/cli/php_cli.c:1340:18
    #13 0xffffaacc777c  (/usr/lib/aarch64-linux-gnu/libc.so.6+0x2777c) (BuildId: c051f80d4fd82e94cd3e72f0eac71ed4f5c0ccbf)
    #14 0xffffaacc7854 in __libc_start_main (/usr/lib/aarch64-linux-gnu/libc.so.6+0x27854) (BuildId: c051f80d4fd82e94cd3e72f0eac71ed4f5c0ccbf)
    #15 0xaaaac6a2986c in _start (/opt/php/nts-asan/bin/php+0x40986c) (BuildId: aced2656c333be293f1ad721ee2fb70efbd41d0b)

SUMMARY: AddressSanitizer: heap-buffer-overflow /usr/local/src/php/ext/pcre/pcre2lib/pcre2_jit_neon_inc.h:267:22 in ffcps_0
Shadow bytes around the buggy address:
  0x60d00011e180: fd fd fd fd fa fa fa fa fa fa fa fa 00 00 00 00
  0x60d00011e200: 00 00 00 00 00 00 00 00 00 00 00 00 04 fa fa fa
  0x60d00011e280: fa fa fa fa fa fa 00 00 00 00 00 00 00 00 00 00
  0x60d00011e300: 00 00 00 00 00 00 04 fa fa fa fa fa fa fa fa fa
  0x60d00011e380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x60d00011e400: 00[fa]fa fa fa fa fa fa fa fa 00 00 00 00 00 00
  0x60d00011e480: 00 00 00 00 00 00 00 00 00 00 00 00 fa fa fa fa
  0x60d00011e500: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
  0x60d00011e580: fd fd fd fd fd fa fa fa fa fa fa fa fa fa fd fd
  0x60d00011e600: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa
  0x60d00011e680: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==7558==ABORTING

Here is an example backtrace of what PHP was doing:

(gdb) zbacktrace
[0xffffa93254e0] preg_replace("/{MAIL:(\S+)}/", "tee $1 >/dev/null", "assert.exception=1\12zend_extension=opcache\12opcache.enable_cli=1\12opcache.jit=tracing\12opca
che.jit_buffer_size=4M\12") [internal function]
[0xffffa93205a0] run_test("'/opt/php/nts-asan/bin/php'", "/usr/local/src/dd-trace-php/profiling/tests/phpt/phpinfo_04.phpt", array(33)[0xffffa9320610]) 
/usr/local/src/dd-trace-php/profiling/tests/run-tests.php:2133 
[0xffffa9320120] run_all_tests(array(9)[0xffffa9320170], array(21)[0xffffa9320180], NULL) /usr/local/src/dd-trace-php/profiling/tests/run-tests.php:1323 
[0xffffa931fcc0] run_worker() /usr/local/src/dd-trace-php/profiling/tests/run-tests.php:1777 
[0xffffa931d8c0] main() /usr/local/src/dd-trace-php/profiling/tests/run-tests.php:177 

My toolchain was built with this docker image:

FROM silkeh/clang:16-bookworm

RUN apt update \
  && apt install -y \
    autoconf \
    bison \
    curl \
    git \
    libcurl4-openssl-dev \
    libedit-dev \
    libfreetype-dev \
    libjpeg-dev \
    libonig-dev \
    libpng-dev \
    libpq-dev \
    libsodium-dev \
    libsqlite3-dev \
    libssl-dev \
    libtool \
    libwebp-dev \
    libxml2-dev \
    libxslt1-dev \
    libzip-dev \
    pkg-config \
    re2c \
    vim \
    zlib1g-dev

RUN cd /usr/local/src \
  && git clone -b 'PHP-8.3' https://github.com/php/php-src.git php \
  && cd php \
  && ./buildconf

RUN mkdir /tmp/build-php \
  && cd /tmp/build-php \
  && /usr/local/src/php/configure \
    --enable-option-checking=fatal \
    --enable-address-sanitizer \
    --prefix=/opt/php/nts-asan \
    --with-config-file-path=/opt/php/nts-asan \
    --with-config-file-scan-dir=/opt/php/nts-asan/conf.d \
    --with-layout=GNU \
    --enable-bcmath \
    --enable-cgi \
    --enable-embed \
    --enable-fpm \
    --enable-ftp \
    --enable-gd \
    --enable-intl \
    --enable-mbstring \
    --enable-opcache \
    --enable-pcntl \
    --enable-soap \
    --enable-sockets \
    --enable-zend-test=static \
    --with-curl \
    --with-ffi \
    --with-fpm-group=www-data \
    --with-fpm-user=www-data \
    --with-freetype \
    --with-jpeg \
    --with-libedit \
    --with-mhash \
    --with-mysqli=mysqlnd \
    --with-openssl \
    --with-pdo-mysql=mysqlnd \
    --with-pdo-pgsql \
    --with-pdo-sqlite \
    --with-pear \
    --with-readline \
    --with-sodium \
    --with-webp \
    --with-xsl \
    --with-zip \
    --with-zlib \
  && make -j 8 all \
  && make install \
  && cd - \
  && rm -fr /tmp/build-php

ENV PATH="$PATH:/opt/php/nts-asan/bin"

WORKDIR /usr/local/src

# The exact tests do not seem to matter, the issue seems to
# be in PHP plus the run-tests.php code, not the tests that
# it executes.
RUN mkdir -v /usr/local/src/tests \
  && cd /usr/local/src/tests \
  && curl \
    -OL https://raw.githubusercontent.com/DataDog/dd-trace-php/master/profiling/tests/phpt/phpinfo_04.phpt \
    -OL https://raw.githubusercontent.com/DataDog/dd-trace-php/master/profiling/tests/phpt/phpinfo_01.phpt \
  && cd .. \
  && cp /usr/local/src/php/run-tests.php .

Inside the container, run:

php run-tests.php --asan -j2 tests

The parallelism of -j2 is required because it takes a different path down run-tests.php.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions