PCRE2Project / pcre2

PCRE2 development is now based here.
Other
922 stars 194 forks source link

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

Closed morrisonlevi closed 1 year ago

morrisonlevi commented 1 year ago

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.

carenas commented 1 year ago

that looks just like #86 which I thought was fixed and released already; can you make sure the vendored php you are using includes the fix?

FWIW sanitizer might be also confused but the fact that vector operations operate outside their buffers since they obviously have to load aligned chunks of memory with a pointer somewhere in the middle.

morrisonlevi commented 1 year ago

Did you mean to link a different issue? As far as I can tell, the stacks look quite different. Even if you meant that issue, the bundled pcre version is 10.42 for the version of PHP I am building, so it should include whatever fix you are referring to as it's the latest released version, yes?

carenas commented 1 year ago

Did you mean to link a different issue?

correct, updated my link; apologies for the confusion.

still, my comment about ASAN stands; AFAIK this is just a buffer over read (affecting aarch64 regardless of OS), which is no reason to crash, as it is expected.

FWIW, all architectures that have SIMD operations enabled (x86, s390x and next release also LoongArch) do the same buffer over reads but since they are done by generated sljit code it is not something that ASAN can "detect".

morrisonlevi commented 1 year ago

I'll try the patch later. As far as ASAN is concerned, it can't tell "on purpose" types of ones apart from mistakes, so it makes sense it would complain.

morrisonlevi commented 1 year ago

I had some travel, and I'm back now. I expect to try this out next week.

morrisonlevi commented 1 year ago

I was able to double-check this today: no warnings, all tests pass 👍🏻