├── .appveyor.yml ├── .appveyor ├── build.cmd ├── build_task.cmd └── install.cmd ├── .gitignore ├── .travis.yml ├── .travis ├── compile.sh └── show-error.sh ├── CREDITS ├── EXPERIMENTAL ├── LICENSE ├── README.md ├── README.zh-CN.md ├── artwork └── var_dump_example.png ├── config.m4 ├── config.w32 ├── php_xmark.h ├── tests ├── 001.phpt ├── 002.phpt ├── 003.phpt ├── 004.phpt ├── 005.phpt ├── 006.phpt ├── 007.phpt ├── 008.phpt ├── 009.phpt ├── 010.phpt ├── 011.phpt ├── 012.phpt ├── 013.phpt ├── 014.phpt ├── 015.phpt ├── 016.phpt ├── 017.phpt ├── 018.phpt ├── 019.phpt ├── 020.phpt ├── 021.phpt ├── 022.phpt ├── 023.phpt ├── 024.phpt ├── base.php ├── issue003.phpt ├── issue005.phpt ├── taint-bug61163.phpt ├── taint-bug61816.phpt ├── taint-bug63100.phpt ├── taint-bug63123.phpt ├── taint-issue004.phpt ├── taint-issue026.phpt ├── taint-issue061.phpt ├── taint002.phpt ├── taint003.phpt ├── taint004.phpt ├── taint005.phpt ├── taint006.phpt ├── taint007.phpt ├── taint008.phpt ├── taint009.phpt ├── taint010.phpt ├── taint011.phpt ├── taint012.phpt ├── taint013.phpt ├── taint014.phpt ├── taint015.phpt ├── taint016.phpt ├── taint017.phpt └── taint018.phpt └── xmark.c /.appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2015 2 | version: '{branch}.{build}' 3 | 4 | environment: 5 | PHP_BUILD_CACHE_BASE_DIR: c:\build-cache 6 | PHP_BUILD_OBJ_DIR: c:\obj 7 | PHP_BUILD_CACHE_SDK_DIR: c:\build-cache\sdk 8 | PHP_BUILD_SDK_BRANCH: php-sdk-2.1.1 9 | SDK_REMOTE: https://github.com/OSTC/php-sdk-binary-tools.git 10 | SDK_BRANCH: php-sdk-2.1.1 11 | 12 | matrix: 13 | - PHP_REL: 7.3 14 | ARCHITECTURE: x64 15 | ZTS_STATE: enable 16 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 17 | PHP_BUILD_CRT: vc15 18 | - PHP_REL: 7.2 19 | ARCHITECTURE: x64 20 | ZTS_STATE: enable 21 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 22 | PHP_BUILD_CRT: vc15 23 | - PHP_REL: 7.1 24 | ARCHITECTURE: x64 25 | ZTS_STATE: enable 26 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 27 | PHP_BUILD_CRT: vc14 28 | - PHP_REL: 7.0 29 | ARCHITECTURE: x64 30 | ZTS_STATE: enable 31 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 32 | PHP_BUILD_CRT: vc14 33 | - PHP_REL: 7.3 34 | ARCHITECTURE: x64 35 | ZTS_STATE: disable 36 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 37 | PHP_BUILD_CRT: vc15 38 | - PHP_REL: 7.2 39 | ARCHITECTURE: x64 40 | ZTS_STATE: disable 41 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 42 | PHP_BUILD_CRT: vc15 43 | - PHP_REL: 7.1 44 | ARCHITECTURE: x64 45 | ZTS_STATE: disable 46 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 47 | PHP_BUILD_CRT: vc14 48 | - PHP_REL: 7.0 49 | ARCHITECTURE: x64 50 | ZTS_STATE: disable 51 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 52 | PHP_BUILD_CRT: vc14 53 | - PHP_REL: 7.2 54 | ARCHITECTURE: x86 55 | ZTS_STATE: enable 56 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 57 | PHP_BUILD_CRT: vc15 58 | - PHP_REL: 7.1 59 | ARCHITECTURE: x86 60 | ZTS_STATE: enable 61 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 62 | PHP_BUILD_CRT: vc14 63 | - PHP_REL: 7.0 64 | ARCHITECTURE: x86 65 | ZTS_STATE: enable 66 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 67 | PHP_BUILD_CRT: vc14 68 | - PHP_REL: 7.2 69 | ARCHITECTURE: x86 70 | ZTS_STATE: disable 71 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 72 | PHP_BUILD_CRT: vc15 73 | - PHP_REL: 7.1 74 | ARCHITECTURE: x86 75 | ZTS_STATE: disable 76 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 77 | PHP_BUILD_CRT: vc14 78 | - PHP_REL: 7.0 79 | ARCHITECTURE: x86 80 | ZTS_STATE: disable 81 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 82 | PHP_BUILD_CRT: vc14 83 | 84 | install: 85 | - .appveyor\install.cmd 86 | 87 | build_script: 88 | - .appveyor\build.cmd -------------------------------------------------------------------------------- /.appveyor/build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | setlocal enableextensions enabledelayedexpansion 4 | set SDK_RUNNER=%PHP_BUILD_CACHE_SDK_DIR%\phpsdk-%PHP_BUILD_CRT%-%ARCHITECTURE%.bat 5 | if not exist "!SDK_RUNNER!" ( 6 | echo "!SDK_RUNNER!" doesn't exist 7 | exit /b 3 8 | ) 9 | 10 | cmd /c !SDK_RUNNER! -t %APPVEYOR_BUILD_FOLDER%\.appveyor\build_task.cmd 11 | 12 | if %errorlevel% neq 0 exit /b 3 13 | endlocal 14 | 15 | exit /b 0 -------------------------------------------------------------------------------- /.appveyor/build_task.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enableextensions enabledelayedexpansion 3 | 4 | cd /D %APPVEYOR_BUILD_FOLDER% 5 | if %errorlevel% neq 0 exit /b 3 6 | 7 | set STABILITY=staging 8 | set DEPS_DIR=%PHP_BUILD_CACHE_BASE_DIR%\deps-%PHP_REL%-%PHP_SDK_VC%-%PHP_SDK_ARCH% 9 | 10 | rem SDK is cached, deps info is cached as well 11 | echo [*] Updating dependencies in %DEPS_DIR% 12 | cmd /c phpsdk_deps --update --no-backup --branch %PHP_REL% --stability %STABILITY% --deps %DEPS_DIR% 13 | if %errorlevel% neq 0 exit /b 3 14 | 15 | rem Something went wrong, most likely when concurrent builds were to fetch deps 16 | rem updates. It might be, that some locking mechanism is needed. 17 | if not exist "%DEPS_DIR%" ( 18 | cmd /c phpsdk_deps --update --force --no-backup --branch %PHP_REL% --stability %STABILITY% --deps %DEPS_DIR% 19 | ) 20 | if %errorlevel% neq 0 exit /b 3 21 | 22 | if "%ZTS_STATE%"=="enable" set ZTS_SHORT=ts 23 | if "%ZTS_STATE%"=="disable" set ZTS_SHORT=nts 24 | if "%ZTS_STATE%"=="enable" set ZTS_IN_FILENAME=-zts 25 | if "%ZTS_STATE%"=="disable" set ZTS_IN_FILENAME=-nts 26 | if "%PHP_SDK_ARCH%"=="x86" set ARCH_IN_FILENAME=-x86 27 | if "%PHP_SDK_ARCH%"=="x64" set ARCH_IN_FILENAME=-x64 28 | 29 | cd /d C:\projects\php-src 30 | 31 | cmd /c buildconf.bat --force 32 | 33 | if %errorlevel% neq 0 exit /b 3 34 | 35 | cmd /c configure.bat --with-mp=auto --enable-cli --enable-pdo --with-sqlite3 --with-pdo-sqlite --%ZTS_STATE%-zts --enable-xmark=shared --enable-object-out-dir=%PHP_BUILD_OBJ_DIR% --with-config-file-scan-dir=%APPVEYOR_BUILD_FOLDER%\build\modules.d --with-prefix=%APPVEYOR_BUILD_FOLDER%\build --with-php-build=%DEPS_DIR% 36 | 37 | if %errorlevel% neq 0 exit /b 3 38 | 39 | nmake /NOLOGO 40 | if %errorlevel% neq 0 exit /b 3 41 | 42 | nmake install 43 | 44 | if %errorlevel% neq 0 exit /b 3 45 | 46 | mkdir c:\tests_tmp 47 | set TEST_PHP_EXECUTABLE=%APPVEYOR_BUILD_FOLDER%\build\php.exe 48 | set TEST_PHP_JUNIT=c:\tests_tmp\tests-junit.xml 49 | 50 | set TEST_PHP_ARGS=-n -d -foo=1 -d extension=%APPVEYOR_BUILD_FOLDER%\build\ext\php_xmark.dll 51 | 52 | echo [*] !TEST_PHP_EXECUTABLE! !TEST_PHP_ARGS! -v 53 | !TEST_PHP_EXECUTABLE! !TEST_PHP_ARGS! -v 54 | 55 | echo [*] !TEST_PHP_EXECUTABLE! -n run-tests.php -q -x --show-diff --show-slow 1000 --set-timeout 120 -g FAIL,XFAIL,BORK,WARN,LEAK,SKIP --temp-source c:\tests_tmp --temp-target c:\tests_tmp %APPVEYOR_BUILD_FOLDER%\tests 56 | !TEST_PHP_EXECUTABLE! -n run-tests.php -q -x --show-diff %APPVEYOR_BUILD_FOLDER%\tests 57 | 58 | set EXIT_CODE=%errorlevel% 59 | powershell -Command "$wc = New-Object 'System.Net.WebClient'; $wc.UploadFile('https://ci.appveyor.com/api/testresults/junit/%APPVEYOR_JOB_ID%', 'c:\tests_tmp\tests-junit.xml')" 60 | if %EXIT_CODE% neq 0 exit /b 3 61 | 62 | cd /d %APPVEYOR_BUILD_FOLDER% 63 | 64 | if not exist "%APPVEYOR_BUILD_FOLDER%\build\ext\php_xmark.dll" exit /b 3 65 | 66 | set ARTIFACT_FILENAME="php_xmark-%APPVEYOR_REPO_TAG_NAME%-%PHP_REL%-%PHP_BUILD_CRT%!ZTS_IN_FILENAME!!ARCH_IN_FILENAME!.zip" 67 | 68 | xcopy %APPVEYOR_BUILD_FOLDER%\LICENSE %APPVEYOR_BUILD_FOLDER%\xmark\ /y /f 69 | echo F|xcopy %APPVEYOR_BUILD_FOLDER%\build\ext\php_xmark.dll %APPVEYOR_BUILD_FOLDER%\xmark\php_xmark.dll /y /f 70 | 7z a %ARTIFACT_FILENAME% %APPVEYOR_BUILD_FOLDER%\xmark\* 71 | appveyor PushArtifact %ARTIFACT_FILENAME% -FileName %ARTIFACT_FILENAME% 72 | endlocal -------------------------------------------------------------------------------- /.appveyor/install.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal enableextensions enabledelayedexpansion 3 | cinst wget 4 | 5 | if not exist "%PHP_BUILD_CACHE_BASE_DIR%" ( 6 | echo [*] Creating %PHP_BUILD_CACHE_BASE_DIR% 7 | mkdir "%PHP_BUILD_CACHE_BASE_DIR%" 8 | ) 9 | 10 | if not exist "%PHP_BUILD_OBJ_DIR%" ( 11 | echo [*] Creating %PHP_BUILD_OBJ_DIR% 12 | mkdir "%PHP_BUILD_OBJ_DIR%" 13 | ) 14 | 15 | if not exist "%PHP_BUILD_CACHE_SDK_DIR%" ( 16 | echo [*] Cloning remote SDK repository 17 | git clone -q --branch %SDK_BRANCH% %SDK_REMOTE% "%PHP_BUILD_CACHE_SDK_DIR%" 2>&1 18 | ) else ( 19 | echo [*] Fetching remote SDK repository 20 | git --git-dir="%PHP_BUILD_CACHE_SDK_DIR%\.git" --work-tree="%PHP_BUILD_CACHE_SDK_DIR%" fetch --prune origin 2>&1 21 | echo [*] Checkout SDK repository branch 22 | git --git-dir="%PHP_BUILD_CACHE_SDK_DIR%\.git" --work-tree="%PHP_BUILD_CACHE_SDK_DIR%" checkout --force %SDK_BRANCH% 23 | ) 24 | 25 | if "%PHP_REL%"=="master" ( 26 | echo [*] git clone -q --depth=1 --branch=%PHP_REL% https://github.com/php/php-src C:\projects\php-src 27 | git clone -q --depth=1 --branch=%PHP_REL% https://github.com/php/php-src C:\projects\php-src 28 | ) else ( 29 | echo [*] git clone -q --depth=1 --branch=PHP-%PHP_REL% https://github.com/php/php-src C:\projects\php-src 30 | git clone -q --depth=1 --branch=PHP-%PHP_REL% https://github.com/php/php-src C:\projects\php-src 31 | ) 32 | 33 | xcopy %APPVEYOR_BUILD_FOLDER% C:\projects\php-src\ext\xmark\ /s /e /y /q 34 | 35 | xcopy %APPVEYOR_BUILD_FOLDER%\LICENSE %APPVEYOR_BUILD_FOLDER%\artifacts\ /y /q 36 | 37 | if "%APPVEYOR%" equ "True" rmdir /s /q C:\cygwin >NUL 2>NUL 38 | if %errorlevel% neq 0 exit /b 3 39 | if "%APPVEYOR%" equ "True" rmdir /s /q C:\cygwin64 >NUL 2>NUL 40 | if %errorlevel% neq 0 exit /b 3 41 | if "%APPVEYOR%" equ "True" rmdir /s /q C:\mingw >NUL 2>NUL 42 | if %errorlevel% neq 0 exit /b 3 43 | if "%APPVEYOR%" equ "True" rmdir /s /q C:\mingw-w64 >NUL 2>NUL 44 | if %errorlevel% neq 0 exit /b 3 45 | 46 | if "%APPVEYOR_REPO_TAG_NAME%"=="" ( 47 | set APPVEYOR_REPO_TAG_NAME=%APPVEYOR_REPO_BRANCH%-%APPVEYOR_REPO_COMMIT:~0,8% 48 | for /f "delims=" %%l in (php_xmark.h) do ( 49 | if not "%%l"=="" ( 50 | set line=%%l 51 | if "!line:~8,17!"=="PHP_XMARK_VERSION" ( 52 | set APPVEYOR_REPO_TAG_NAME=!line:~27,-1! 53 | ) 54 | ) 55 | ) 56 | echo [*] Final repo tag name: !APPVEYOR_REPO_TAG_NAME! 57 | 58 | appveyor SetVariable -Name APPVEYOR_REPO_TAG_NAME -Value !APPVEYOR_REPO_TAG_NAME! 59 | ) 60 | endlocal -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | *.lo 3 | *.la 4 | .libs 5 | acinclude.m4 6 | aclocal.m4 7 | autom4te.cache 8 | build 9 | config.guess 10 | config.h 11 | config.h.in 12 | config.log 13 | config.nice 14 | config.status 15 | config.sub 16 | configure 17 | configure.ac 18 | include 19 | install-sh 20 | libtool 21 | ltmain.sh 22 | Makefile 23 | Makefile.fragments 24 | Makefile.global 25 | Makefile.objects 26 | missing 27 | mkinstalldirs 28 | modules 29 | run-tests.php 30 | tests/*/*.diff 31 | tests/*/*.out 32 | tests/*/*.php 33 | tests/*/*.exp 34 | tests/*/*.log 35 | tests/*/*.sh 36 | tests/php.ini 37 | .idea 38 | cmake-build-debug/ 39 | CMakeLists.txt 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.0 5 | - 7.1 6 | - 7.2 7 | - 7.3 8 | - nightly 9 | 10 | matrix: 11 | allow_failures: 12 | - php: nightly 13 | 14 | env: 15 | - REPORT_EXIT_STATUS=1 NO_INTERACTION=1 16 | 17 | before_script: 18 | - ./.travis/compile.sh 19 | 20 | script: make test 21 | 22 | after_script: 23 | - ./.travis/show-error.sh 24 | -------------------------------------------------------------------------------- /.travis/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | phpize && ./configure && make -------------------------------------------------------------------------------- /.travis/show-error.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for i in tests/*.diff; do 4 | echo "==========================================================================" 5 | echo $i 6 | echo "--------------------------------------------------------------------------" 7 | cat $i 8 | echo "==========================================================================" 9 | echo 10 | done -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | xmark 2 | -------------------------------------------------------------------------------- /EXPERIMENTAL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fate0/xmark/34dd79d3e38dfb7f22c67eaedaa540a4cd88aee6/EXPERIMENTAL -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xmark 2 | 3 | [![Build Status](https://travis-ci.org/fate0/xmark.svg?branch=master)](https://travis-ci.org/fate0/xmark) 4 | [![AppVeyor Status](https://ci.appveyor.com/api/projects/status/github/fate0/xmark?branch=master&svg=true)](https://ci.appveyor.com/project/fate0/xmark/) 5 | ![GitHub](https://img.shields.io/github/license/fate0/xmark.svg) 6 | 7 | [中文文档](https://github.com/fate0/xmark/blob/master/README.zh-CN.md) 8 | 9 | ## Table of Contents 10 | 11 | * [Introduction](#introduction) 12 | * [Installation](#installation) 13 | * [Example](#example) 14 | * [API](#api) 15 | * [OPCODE](#opcode) 16 | * [PHP configuration](#php-configuration) 17 | * [Note](#note) 18 | * [Ref](#ref) 19 | 20 | 21 | ### Introduction 22 | 23 | xmark is a PHP7 extension that provides the following features: 24 | 25 | * It can mark string variables 26 | * It can hook most functions/classes 27 | * It can Hook parts of the opcodes 28 | 29 | ### Installation 30 | 31 | * linux: 32 | 33 | ``` 34 | phpize 35 | ./configure 36 | make 37 | ``` 38 | 39 | * windows 40 | 41 | [download](https://github.com/fate0/xmark/releases) 42 | 43 | ### Example 44 | 45 | example: 46 | ``` php 47 | hi(); 280 | ``` 281 | 282 | when `array_map` is called, `zend_get_executed_scope` will look up the last use function in the call stack and then 283 | determine if the user function has permission to call the callback. so it may cause the the call to `array_map` to fail. 284 | the same problem occurs in other internal functions that accept callback parameter. 285 | 286 | in summary, if a function depends on or will change the scope of the caller, 287 | then you should carefully determine whether the function can still be hooked. 288 | 289 | 290 | ### Ref 291 | 292 | * [taint](https://github.com/laruence/taint) 293 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # xmark 2 | 3 | [![Build Status](https://travis-ci.org/fate0/xmark.svg?branch=master)](https://travis-ci.org/fate0/xmark) 4 | [![AppVeyor Status](https://ci.appveyor.com/api/projects/status/github/fate0/xmark?branch=master&svg=true)](https://ci.appveyor.com/project/fate0/xmark/) 5 | ![GitHub](https://img.shields.io/github/license/fate0/xmark.svg) 6 | 7 | 8 | ## 目录 9 | 10 | * [介绍](#介绍) 11 | * [安装](#安装) 12 | * [例子](#例子) 13 | * [API](#api) 14 | * [OPCODE](#opcode) 15 | * [PHP 配置](#php-配置) 16 | * [注意事项](#注意事项) 17 | * [Ref](#ref) 18 | 19 | 20 | ### 介绍 21 | 22 | 一个 PHP7 扩展,所提供的功能为: 23 | 24 | * 能够对字符串变量进行打标记 25 | * 能够 Hook 绝大多数函数/类 26 | * 能够 Hook 部分 opcode 27 | 28 | ### 安装 29 | 30 | * linux: 31 | 32 | ``` 33 | phpize 34 | ./configure 35 | make 36 | ``` 37 | 38 | * windows 39 | 40 | [download](https://github.com/fate0/xmark/releases) 41 | 42 | ### 例子 43 | 44 | 代码: 45 | ``` php 46 | hi(); 281 | ``` 282 | 当在方法中调用 `array_map` 的时候,会使用 `zend_get_executed_scope` 查找调用栈中最后一个 user function, 283 | 然后判断这个 user function 是否有权限调用 callback,所以可能会导致 `array_map` 调用失败, 284 | 同样的问题还出现在其他接受 callback 参数的 internal function 中。 285 | 286 | 总而言之,如果一个函数依赖或者将改变调用者的 scope,那就要仔细判断该函数还能不能直接进行 Hook。 287 | 288 | 289 | ### 引用 290 | 291 | * [taint](https://github.com/laruence/taint) 292 | -------------------------------------------------------------------------------- /artwork/var_dump_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fate0/xmark/34dd79d3e38dfb7f22c67eaedaa540a4cd88aee6/artwork/var_dump_example.png -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | 3 | 4 | PHP_ARG_ENABLE(xmark, whether to enable xmark support, 5 | [ --enable-xmark Enable xmark support]) 6 | 7 | 8 | if test -z "$PHP_DEBUG"; then 9 | AC_ARG_ENABLE(debug, 10 | [ --enable-debug compile with debugging symbols], 11 | [ PHP_DEBUG=$enableval ], 12 | [ PHP_DEBUG=no ]) 13 | fi 14 | 15 | if test "$PHP_TAINT" != "no"; then 16 | PHP_NEW_EXTENSION(xmark, xmark.c, $ext_shared) 17 | fi 18 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | ARG_ENABLE("xmark", "enable xmark support", "no"); 5 | 6 | if (PHP_XMARK != "no") { 7 | EXTENSION("xmark", "xmark.c"); 8 | } 9 | 10 | -------------------------------------------------------------------------------- /php_xmark.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2017 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifndef PHP_XMARK_H 22 | #define PHP_XMARK_H 23 | 24 | extern zend_module_entry xmark_module_entry; 25 | #define phpext_xmark_ptr &xmark_module_entry 26 | 27 | #define PHP_XMARK_VERSION "1.0.0-dev" 28 | 29 | #ifdef PHP_WIN32 30 | # define PHP_XMARK_API __declspec(dllexport) 31 | #elif defined(__GNUC__) && __GNUC__ >= 4 32 | # define PHP_XMARK_API __attribute__ ((visibility("default"))) 33 | #else 34 | # define PHP_XMARK_API 35 | #endif 36 | 37 | #ifdef ZTS 38 | #include "TSRM.h" 39 | #endif 40 | 41 | #if PHP_VERSION_ID < 70300 42 | # define IS_XMARK_FLAG (1<<6) 43 | # define XMARK_FLAG(str) (GC_FLAGS((str)) |= IS_XMARK_FLAG) 44 | # define XCLEAR_FLAG(str) (GC_FLAGS((str)) &= ~IS_XMARK_FLAG) 45 | # define XCHECK_FLAG(str) (GC_FLAGS((str)) & IS_XMARK_FLAG) 46 | #else 47 | # define EX_CONSTANT(op) RT_CONSTANT(EX(opline), op) 48 | # define IS_XMARK_FLAG (1<<5) 49 | # define XMARK_FLAG(str) GC_ADD_FLAGS(str, IS_XMARK_FLAG) 50 | # define XCLEAR_FLAG(str) GC_DEL_FLAGS(str, IS_XMARK_FLAG) 51 | # define XCHECK_FLAG(str) (GC_FLAGS((str)) & IS_XMARK_FLAG) 52 | #endif 53 | 54 | 55 | #define XMARK_OP1_TYPE(opline) ((opline)->op1_type) 56 | #define XMARK_OP2_TYPE(opline) ((opline)->op2_type) 57 | 58 | 59 | #define XMARK_IS_FUNCTION 1 << 0 60 | #define XMARK_IS_CLASS 1 << 1 61 | 62 | 63 | #if PHP_VERSION_ID < 70100 64 | #define XMARK_RET_USED(opline) (!((opline)->result_type & EXT_TYPE_UNUSED)) 65 | #define XMARK_ISERR(var) ((var) == &EG(error_zval)) 66 | #define XMARK_ERR_ZVAL(var) ((var) = &EG(error_zval)) 67 | #else 68 | #define XMARK_RET_USED(opline) ((opline)->result_type != IS_UNUSED) 69 | #define XMARK_ISERR(var) (Z_ISERROR_P(var)) 70 | #define XMARK_ERR_ZVAL(var) (ZVAL_ERROR(var)) 71 | #endif 72 | 73 | 74 | ZEND_BEGIN_MODULE_GLOBALS(xmark) 75 | zend_bool enable; 76 | zend_bool enable_rename; 77 | zend_bool in_callback; 78 | char *rename_functions; 79 | char *rename_classes; 80 | HashTable callbacks; 81 | ZEND_END_MODULE_GLOBALS(xmark) 82 | 83 | /* Always refer to the globals in your function as XMARK_G(variable). 84 | You are encouraged to rename these macros something shorter, see 85 | examples in any other php module directory. 86 | */ 87 | #define XMARK_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(xmark, v) 88 | 89 | #if defined(ZTS) && defined(COMPILE_DL_XMARK) 90 | ZEND_TSRMLS_CACHE_EXTERN() 91 | #endif 92 | 93 | #if PHP_VERSION_ID < 70200 94 | static zend_always_inline zend_string *zend_string_init_interned(const char *str, size_t len, int persistent) 95 | { 96 | zend_string *ret = zend_string_init(str, len, persistent); 97 | 98 | return zend_new_interned_string(ret); 99 | } 100 | #endif 101 | 102 | PHP_FUNCTION(xmark); 103 | PHP_FUNCTION(xcheck); 104 | PHP_FUNCTION(xclear); 105 | PHP_FUNCTION(xrename_function); 106 | PHP_FUNCTION(xrename_class); 107 | PHP_FUNCTION(xregister_opcode_callback); 108 | 109 | static void rename_from_ini_value(HashTable *ht, const char *ini_name, int type); 110 | static zend_always_inline int xmark_zstr(zval *z_str); 111 | static zend_always_inline Bucket *rename_hash_key(HashTable *ht, zend_string *orig_name, zend_string *new_name, int type); 112 | static zend_always_inline Bucket *rename_hash_str_key(HashTable *ht, const char *orig_name, const char *new_name, int type); 113 | 114 | #endif /* PHP_XMARK_H */ 115 | 116 | 117 | /* 118 | * Local variables: 119 | * tab-width: 4 120 | * c-basic-offset: 4 121 | * End: 122 | * vim600: noet sw=4 ts=4 fdm=marker 123 | * vim<600: noet sw=4 ts=4 124 | */ 125 | -------------------------------------------------------------------------------- /tests/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for xmark presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 20 | --EXPECT-- 21 | xmark extension is available 22 | -------------------------------------------------------------------------------- /tests/002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check xmark(), xcheck(), xclear() 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | include_path={PWD} 8 | auto_prepend_file=base.php 9 | --FILE-- 10 | 36 | --EXPECTF-- 37 | bool(true) 38 | bool(false) 39 | bool(false) 40 | bool(true) 41 | bool(true) 42 | echo_handler:xmark: 'aaaa' 1 43 | aaaa -------------------------------------------------------------------------------- /tests/003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check xrename_function 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | 27 | --EXPECTF-- 28 | before rename 29 | string(6) "before" 30 | int(123) 31 | after rename 32 | string(5) "after" 33 | int(321) 34 | -------------------------------------------------------------------------------- /tests/004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check rename internal function 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.rename_functions=phpinfo:myphpinfo, print_r:myprint_r 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | 26 | --EXPECTF-- 27 | rename phpinfo 28 | rename print_r -------------------------------------------------------------------------------- /tests/005.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check xrename_class 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | say(); 23 | 24 | 25 | ?> 26 | --EXPECTF-- 27 | I am hello -------------------------------------------------------------------------------- /tests/006.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check do_call 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | include_path={PWD} 8 | auto_prepend_file=base.php 9 | --FILE-- 10 | 48 | --EXPECTF-- 49 | string(25) "xregister_opcode_callback" 50 | string(25) "xregister_opcode_callback" 51 | string(5) "hallo" 52 | now in hallo -------------------------------------------------------------------------------- /tests/007.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check clear run_time_cache 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | 41 | --EXPECTF-- 42 | add_filter hello_tag1 43 | my_add_filter hello_tag1 44 | my_add_filter hello_tag2 45 | my_add_filter hello_tag3 -------------------------------------------------------------------------------- /tests/008.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check Illegal string offset 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | include_path={PWD} 8 | auto_prepend_file=base.php 9 | --FILE-- 10 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | say(); 37 | 38 | --EXPECTF-- 39 | before rename 40 | string(6) "before" 41 | int(123) 42 | after rename 43 | string(5) "after" 44 | int(321) 45 | I am hello -------------------------------------------------------------------------------- /tests/010.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check recalc stack size 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | --FILE-- 9 | 28 | --EXPECTF-- 29 | object(SQLite3)#1 (0) { 30 | } -------------------------------------------------------------------------------- /tests/011.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check ZEND_ECHO 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | --FILE-- 9 | say($d); 43 | echo $a; 44 | 45 | $f = array(1, 2, 3, 4); 46 | echo $f; 47 | 48 | ?> 49 | --EXPECTF-- 50 | in echo_handler hello 51 | hello 52 | in echo_handler hello 53 | hello 54 | in echo_handler test 55 | test 56 | in echo_handler test 57 | test 58 | in echo_handler test 59 | test 60 | in echo_handler A 61 | A 62 | in echo_handler ArrayArray -------------------------------------------------------------------------------- /tests/012.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check ZEND_EXIT 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | --FILE-- 9 | 23 | --EXPECTF-- 24 | in exit_handler quit 25 | quit -------------------------------------------------------------------------------- /tests/013.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check ZEND_INIT_METHOD_CALL 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | --FILE-- 9 | say()\n"; 22 | } 23 | } 24 | 25 | 26 | $a = new A(); 27 | $a->say(); 28 | 29 | ?> 30 | --EXPECTF-- 31 | in init_method_call_handler say 32 | in class A->say() -------------------------------------------------------------------------------- /tests/014.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check ZEND_INIT_USER_CALL 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | --FILE-- 9 | 23 | string(8) "my_class" 24 | [1]=> 25 | string(9) "my_method" 26 | } -------------------------------------------------------------------------------- /tests/015.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check ZEND_INIT_DYNAMIC_CALL 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | --FILE-- 9 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | --FILE-- 9 | 36 | --EXPECTF-- 37 | include: string hello.php 38 | eval: string 1+1; 39 | eval: object 2+2; -------------------------------------------------------------------------------- /tests/017.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check ZEND_CONCAT 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | --FILE-- 9 | 27 | --EXPECTF-- 28 | string(5) "test1" 29 | string(5) "test4" 30 | string(14) "test1test4tail" 31 | string(5) "test2" 32 | string(23) "test1test4tailtest2tail" 33 | string(5) "test3" 34 | string(32) "test1test4tailtest2tailtest3tail" -------------------------------------------------------------------------------- /tests/018.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check ZEND_FAST_CONCAT 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | --FILE-- 9 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | --FILE-- 9 | 25 | --EXPECTF-- 26 | string(4) "test" 27 | string(4) " fun" 28 | string(12) "test funtail" -------------------------------------------------------------------------------- /tests/020.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check ZEND_ROPE_END 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | --FILE-- 9 | 43 | --EXPECTF-- 44 | count: 100 45 | string(29) "hello from 99 from nihaoHELLO" 46 | count: 201 47 | string(29) "hello from 99 from ArrayHELLO" -------------------------------------------------------------------------------- /tests/021.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check ZEND_DO_FCALL 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | --FILE-- 9 | 28 | string(4) "test" 29 | [1]=> 30 | string(1) "t" 31 | } -------------------------------------------------------------------------------- /tests/022.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check ZEND_DO_ICALL 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | --FILE-- 9 | 24 | string(4) "test" 25 | [1]=> 26 | string(1) "t" 27 | } -------------------------------------------------------------------------------- /tests/023.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check ZEND_DO_UCALL 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | --FILE-- 9 | 25 | --EXPECTF-- 26 | string(1) "x" 27 | array(3) { 28 | [0]=> 29 | string(4) "xxxx" 30 | [1]=> 31 | int(123) 32 | [2]=> 33 | array(1) { 34 | [0]=> 35 | string(3) "aaa" 36 | } 37 | } -------------------------------------------------------------------------------- /tests/024.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check ZEND_DO_FCALL_BY_NAME 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.enable_rename=1 8 | --FILE-- 9 | 32 | string(2) "aa" 33 | [1]=> 34 | int(1) 35 | [2]=> 36 | array(2) { 37 | [0]=> 38 | string(1) "x" 39 | [1]=> 40 | int(2) 41 | } 42 | } 43 | hello -------------------------------------------------------------------------------- /tests/base.php: -------------------------------------------------------------------------------- 1 | &$value) { 8 | xmark_var($value, $recursive); 9 | } 10 | } 11 | } 12 | 13 | 14 | function xcheck_var($var, $recursive=true) { 15 | if (is_string($var)) { 16 | return xcheck($var); 17 | } elseif (is_array($var) && $recursive) { 18 | foreach ($var as $key => &$value) { 19 | if (xcheck_var($value, $recursive)) return true; 20 | } 21 | } 22 | 23 | return false; 24 | } 25 | 26 | 27 | function repr($var) { 28 | return addcslashes(var_export($var, true), "\n\r\t"); 29 | } 30 | 31 | 32 | function echo_handler($string) { 33 | if (is_string($string) && xcheck($string)) { 34 | echo "echo_handler:xmark: ".repr($string)." ".xcheck($string)."\n"; 35 | } 36 | } 37 | 38 | function exit_handler($string) { 39 | if (is_string($string) && xcheck($string)) { 40 | echo "exit_handler:xmark: ".repr($string)." ".xcheck($string)."\n"; 41 | } 42 | } 43 | 44 | function init_user_dynamic_call_handler($funcname) { 45 | if (is_string($funcname)) { 46 | if (xcheck($funcname)) { 47 | echo "init_user_dynamic_call_handler:xcheck: ".repr($funcname)." ".xcheck($funcname)."\n"; 48 | } 49 | } else if (is_array($funcname)) { 50 | if (xcheck($funcname[0])) { 51 | echo "init_user_dynamic_call_handler:xcheck: ".repr($funcname[0])." ".xcheck($funcname[0])."\n"; 52 | } 53 | if (xcheck($funcname[1])) { 54 | echo "init_user_dynamic_call_handler:xcheck: ".repr($funcname[1])." ".xcheck($funcname[1])."\n"; 55 | } 56 | } 57 | } 58 | 59 | function include_or_eval_handler($param) { 60 | if (xcheck($param)) { 61 | echo "include_or_eval_handler:xmark: ".repr($param)." ".xcheck($param)."\n"; 62 | } 63 | } 64 | 65 | function concat_handler($param1, $param2) { 66 | $result = $param1.$param2; 67 | 68 | if (xcheck($param1) || xcheck($param2)) { 69 | xmark($result); 70 | } 71 | 72 | return $result; 73 | } 74 | 75 | function rope_end_handler($params) { 76 | $result = implode($params); 77 | if (xcheck_var($params)) { 78 | xmark($result); 79 | } 80 | return $result; 81 | } 82 | 83 | 84 | function do_fcall($call, $params) { 85 | // do nothing 86 | } 87 | 88 | function do_icall($call, $params) { 89 | // do nothing 90 | } 91 | 92 | function do_fcall_by_name($call, $params) { 93 | // do nothing 94 | } 95 | 96 | 97 | xregister_opcode_callback(XMARK_ECHO, 'echo_handler'); 98 | xregister_opcode_callback(XMARK_EXIT, 'exit_handler'); 99 | xregister_opcode_callback(XMARK_INIT_METHOD_CALL, 'init_user_dynamic_call_handler'); 100 | xregister_opcode_callback(XMARK_INIT_USER_CALL, 'init_user_dynamic_call_handler'); 101 | xregister_opcode_callback(XMARK_INIT_DYNAMIC_CALL, 'init_user_dynamic_call_handler'); 102 | xregister_opcode_callback(XMARK_INCLUDE_OR_EVAL, "include_or_eval_handler"); 103 | xregister_opcode_callback(XMARK_CONCAT, 'concat_handler'); 104 | xregister_opcode_callback(XMARK_FAST_CONCAT, 'concat_handler'); 105 | xregister_opcode_callback(XMARK_ASSIGN_CONCAT, "concat_handler"); 106 | xregister_opcode_callback(XMARK_ROPE_END, 'rope_end_handler'); 107 | xregister_opcode_callback(XMARK_DO_FCALL, 'do_fcall'); 108 | xregister_opcode_callback(XMARK_DO_ICALL, 'do_icall'); 109 | xregister_opcode_callback(XMARK_DO_FCALL_BY_NAME, 'do_fcall_by_name'); 110 | 111 | ?> -------------------------------------------------------------------------------- /tests/issue003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for Reflection::getName 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.rename_functions=system:prvd_system 8 | --FILE-- 9 | getName(); 13 | var_dump($name); 14 | 15 | 16 | ?> 17 | --EXPECT-- 18 | string(11) "prvd_system" 19 | -------------------------------------------------------------------------------- /tests/issue005.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for class internal properties 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | error_reporting=off 7 | xmark.enable=1 8 | xmark.rename_classes=mysqli:prvd_mysqli 9 | --FILE-- 10 | 16 | --EXPECTF-- 17 | object(mysqli)#%d (%d) { 18 | ["affected_rows"]=> 19 | %s 20 | ["client_info"]=> 21 | %s 22 | ["client_version"]=> 23 | int(%d) 24 | ["connect_errno"]=> 25 | int(%d) 26 | ["connect_error"]=> 27 | string(%d) "%s" 28 | ["errno"]=> 29 | %s 30 | ["error"]=> 31 | %s 32 | ["error_list"]=> 33 | %s 34 | ["field_count"]=> 35 | %s 36 | ["host_info"]=> 37 | %s 38 | ["info"]=> 39 | %s 40 | ["insert_id"]=> 41 | %s 42 | ["server_info"]=> 43 | %s 44 | ["server_version"]=> 45 | %s 46 | ["stat"]=> 47 | %s 48 | ["sqlstate"]=> 49 | %s 50 | ["protocol_version"]=> 51 | %s 52 | ["thread_id"]=> 53 | %s 54 | ["warning_count"]=> 55 | %s 56 | } -------------------------------------------------------------------------------- /tests/taint-bug61163.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Bug #61163 (Passing and using tainted data in specific way crashes) 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | include_path={PWD} 8 | auto_prepend_file=base.php 9 | --FILE-- 10 | 5 | --INI-- 6 | xmark.enable=1 7 | include_path={PWD} 8 | auto_prepend_file=base.php 9 | --FILE-- 10 | foo = "this is"; 19 | $c->foo .= $b[0]; 20 | echo $b[0]; // Segmentation fault 21 | var_dump(xcheck($c->foo)); 22 | 23 | ?> 24 | --EXPECT-- 25 | bool(true) 26 | echo_handler:xmark: 'tainted string.\n' 1 27 | tainted string. 28 | bool(true) -------------------------------------------------------------------------------- /tests/taint-bug63100.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Bug #63100 (array_walk_recursive behaves wrongly when taint enabled) 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | include_path={PWD} 8 | auto_prepend_file=base.php 9 | --FILE-- 10 | "; 14 | xmark($a[0]); 15 | 16 | function xxx(&$item) { 17 | $item = htmlspecialchars($item); 18 | } 19 | 20 | array_walk_recursive($a, "xxx"); 21 | 22 | echo $a[0]; 23 | 24 | ?> 25 | --EXPECT-- 26 | tainted string<> -------------------------------------------------------------------------------- /tests/taint-bug63123.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Bug #63123 (Hash pointer should be reset at the end of function:php_taint_mark_strings) 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.rename_functions=explode:my_explode 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | $val) { 32 | echo $val; 33 | } 34 | 35 | 36 | ?> 37 | --EXPECT-- 38 | echo_handler:xmark: 'a\n' 1 39 | a 40 | echo_handler:xmark: 'b\n' 1 41 | b -------------------------------------------------------------------------------- /tests/taint-issue004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ISSUE #4 (wrong op fetched) 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | include_path={PWD} 8 | auto_prepend_file=base.php 9 | --FILE-- 10 | "ccc")); 14 | $a = $b; 15 | } 16 | 17 | $c = "xxx"; 18 | xmark($c); 19 | dummy($c); 20 | var_dump($c); 21 | 22 | ?> 23 | --EXPECT-- 24 | string(3) "ccc" -------------------------------------------------------------------------------- /tests/taint-issue026.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | ISSUE #26 (PDO checking doesn't work) 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.rename_classes=PDO:my_PDO 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | prepare($sql); 34 | $stmt = $db->query($sql); 35 | 36 | ?> 37 | --EXPECT-- 38 | prepare-xcheck: 1 39 | query-xcheck: 1 -------------------------------------------------------------------------------- /tests/taint-issue061.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | TAINT ISSUE #061 (PHP 7.2.6 SIGSEGV) 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.rename_functions=substr:my_substr, trim:my_trim, rtrim:my_rtrim, ltrim:my_ltrim, implode:my_implode 8 | --FILE-- 9 | 38 | --EXPECT-- 39 | string(2) "ab" 40 | string(2) "ab" 41 | string(2) "ab" 42 | string(3) "abc" 43 | string(3) "abc" -------------------------------------------------------------------------------- /tests/taint002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check Taint function 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.rename_functions=file_put_contents:my_file_put_contents 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | 37 | --EXPECT-- 38 | echo_handler:xmark: 'tainted string\n' 1 39 | tainted string 40 | file_put_contents:xcheck: data 1 41 | tainted string+ 42 | include_or_eval_handler:xmark: 'return \'tainted string+\';' 1 -------------------------------------------------------------------------------- /tests/taint003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check Taint with ternary 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | include_path={PWD} 8 | auto_prepend_file=base.php 9 | --FILE-- 10 | 24 | --EXPECT-- 25 | echo_handler:xmark: 'tainted string' 1 26 | tainted string 27 | bool(true) 28 | exit_handler:xmark: 'tainted stringxxxx' 1 29 | tainted stringxxxx -------------------------------------------------------------------------------- /tests/taint004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check Taint with eval 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | include_path={PWD} 8 | auto_prepend_file=base.php 9 | --FILE-- 10 | 19 | --EXPECT-- 20 | exit_handler:xmark: 'tainted string.' 1 21 | tainted string. -------------------------------------------------------------------------------- /tests/taint005.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check Taint with separation 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | include_path={PWD} 8 | auto_prepend_file=base.php 9 | --FILE-- 10 | 25 | --EXPECT-- 26 | echo_handler:xmark: 'tainted string\n' 1 27 | tainted string 28 | echo_handler:xmark: 'tainted string\n' 1 29 | tainted string 30 | echo_handler:xmark: 'tainted string\n' 1 31 | tainted string 32 | echo_handler:xmark: 'tainted string\n' 1 33 | tainted string -------------------------------------------------------------------------------- /tests/taint006.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check Taint with send_var/send_ref 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | include_path={PWD} 8 | auto_prepend_file=base.php 9 | --FILE-- 10 | 46 | --EXPECT-- 47 | ======= normal test ====== 48 | echo_handler:xmark: 'a tainted string\n' 1 49 | a tainted string 50 | echo_handler:xmark: 'a tainted string\n' 1 51 | a tainted string 52 | ======= normal a&b ====== 53 | echo_handler:xmark: 'a tainted string\n' 1 54 | a tainted string 55 | echo_handler:xmark: 'a tainted string\n' 1 56 | a tainted string 57 | ======= normal c&d ====== 58 | echo_handler:xmark: 'c tainted string\n' 1 59 | c tainted string 60 | echo_handler:xmark: 'c tainted string\n' 1 61 | c tainted string -------------------------------------------------------------------------------- /tests/taint007.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check Taint with functions 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.rename_functions=sprintf:my_sprintf, vsprintf:my_vsprintf, explode:my_explode, implode:my_implode, join:my_join, trim:my_trim, rtrim:my_rtrim, ltrim:my_ltrim 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | 132 | --EXPECTF-- 133 | bool(true) 134 | bool(true) 135 | bool(true) 136 | bool(true) 137 | bool(true) 138 | bool(true) 139 | bool(true) 140 | bool(true) -------------------------------------------------------------------------------- /tests/taint008.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check Taint with more functions 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.rename_functions=strstr:my_strstr, substr:my_substr, str_replace:my_str_replace, str_ireplace:my_str_ireplace, str_pad:my_str_pad, strtolower:my_strtolower, strtoupper:my_strtoupper 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | 111 | --EXPECTF-- 112 | bool(true) 113 | bool(true) 114 | bool(true) 115 | bool(true) 116 | bool(true) 117 | bool(false) 118 | bool(true) 119 | bool(true) -------------------------------------------------------------------------------- /tests/taint009.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Fixed bug that tainted info lost if a string is parsed by htmlspecialchars 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | include_path={PWD} 8 | auto_prepend_file=base.php 9 | --FILE-- 10 | 22 | --EXPECTF-- 23 | bool(false) 24 | bool(true) 25 | -------------------------------------------------------------------------------- /tests/taint010.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check Taint with dim assign contact 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | include_path={PWD} 8 | auto_prepend_file=base.php 9 | --FILE-- 10 | foo = "this is"; 20 | $c->foo .= $a; 21 | 22 | var_dump(xcheck($c->foo)); 23 | 24 | ?> 25 | --EXPECT-- 26 | bool(true) 27 | bool(true) -------------------------------------------------------------------------------- /tests/taint011.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check assign_ref and global keyword 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | include_path={PWD} 8 | auto_prepend_file=base.php 9 | --FILE-- 10 | 24 | --EXPECT-- 25 | echo_handler:xmark: 'tainted string\n' 1 26 | tainted string 27 | echo_handler:xmark: 'tainted string\n' 1 28 | tainted string 29 | -------------------------------------------------------------------------------- /tests/taint012.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check dirname, basename, pathinfo 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.rename_functions=dirname:x_dirname, basename:x_basename, pathinfo:x_pathinfo 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | 60 | --EXPECT-- 61 | ====== dirname ======= 62 | echo_handler:xmark: '/home/fate0/playstation' 1 63 | /home/fate0/playstation 64 | ====== basename ======= 65 | echo_handler:xmark: 'assassin\'s creed' 1 66 | assassin's creed 67 | ====== pathinfo1 ======= 68 | echo_handler:xmark: 'assassin\'s creed' 1 69 | assassin's creed 70 | ====== pathinfo2 ======= 71 | echo_handler:xmark: '/home/fate0/playstation' 1 72 | /home/fate0/playstation 73 | ====== end ======= -------------------------------------------------------------------------------- /tests/taint013.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check file, file_get_contents 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.rename_functions=file_get_contents:my_file_get_contents, file:my_file 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | 40 | --EXPECTF-- 41 | file:xcheck: 1 42 | file_get_contents:xcheck: 1 -------------------------------------------------------------------------------- /tests/taint014.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check function call 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | include_path={PWD} 8 | auto_prepend_file=base.php 9 | --FILE-- 10 | $fname(); 29 | call_user_func(array($d, $fname)); 30 | 31 | 32 | ?> 33 | --EXPECTF-- 34 | init_user_dynamic_call_handler:xcheck: 'test' 1 35 | init_user_dynamic_call_handler:xcheck: 'test' 1 36 | init_user_dynamic_call_handler:xcheck: 'test' 1 37 | init_user_dynamic_call_handler:xcheck: 'test' 1 38 | init_user_dynamic_call_handler:xcheck: 'test' 1 39 | -------------------------------------------------------------------------------- /tests/taint015.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check preg_replace_callback 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.rename_functions=preg_replace_callback:my_preg_replace_callback 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | 31 | --EXPECTF-- 32 | preg_replace_callback:xcheck: 1 -------------------------------------------------------------------------------- /tests/taint016.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check header 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.rename_functions=header:my_header 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | 29 | --EXPECT-- 30 | header:xcheck: 1 31 | 32 | -------------------------------------------------------------------------------- /tests/taint017.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check unerialize 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.rename_functions=unserialize:my_unserialize 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | 29 | --EXPECT-- 30 | unserialize:xcheck: 1 -------------------------------------------------------------------------------- /tests/taint018.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check SQLite3 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | xmark.enable=1 7 | xmark.rename_classes=SQLite3:MySQLite3 8 | include_path={PWD} 9 | auto_prepend_file=base.php 10 | --FILE-- 11 | prepare($sql); 35 | $db->query($sql); 36 | 37 | ?> 38 | --EXPECT-- 39 | prepare-xcheck: 1 40 | query-xcheck: 1 -------------------------------------------------------------------------------- /xmark.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2017 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: fate0 | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include "config.h" 21 | #endif 22 | 23 | 24 | #include "php.h" 25 | #include "zend_compile.h" 26 | #include "zend_types.h" 27 | #include "php_ini.h" 28 | #include "ext/standard/info.h" 29 | #include "php_xmark.h" 30 | 31 | ZEND_DECLARE_MODULE_GLOBALS(xmark) 32 | 33 | 34 | /* These are most copied from zend_execute.c: zend_fetch_dimension_address */ 35 | static int php_xmark_make_real_object(zval *object) /* {{{ */ { 36 | if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { 37 | if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE)) { 38 | /* nothing to destroy */ 39 | } else if (EXPECTED((Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) { 40 | zval_ptr_dtor_nogc(object); 41 | } else { 42 | return 0; 43 | } 44 | object_init(object); 45 | zend_error(E_WARNING, "Creating default object from empty value"); 46 | } 47 | return 1; 48 | } 49 | /* }}} */ 50 | 51 | static zend_long php_xmark_check_string_offset(zval *dim, int type) /* {{{ */ { 52 | zend_long offset; 53 | 54 | try_again: 55 | if (UNEXPECTED(Z_TYPE_P(dim) != IS_LONG)) { 56 | switch(Z_TYPE_P(dim)) { 57 | case IS_STRING: 58 | if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, -1)) { 59 | break; 60 | } 61 | if (type != BP_VAR_UNSET) { 62 | zend_error(E_WARNING, "Illegal string offset '%s'", Z_STRVAL_P(dim)); 63 | } 64 | break; 65 | case IS_DOUBLE: 66 | case IS_NULL: 67 | case IS_FALSE: 68 | case IS_TRUE: 69 | zend_error(E_NOTICE, "String offset cast occurred"); 70 | break; 71 | case IS_REFERENCE: 72 | dim = Z_REFVAL_P(dim); 73 | goto try_again; 74 | default: 75 | zend_error(E_WARNING, "Illegal offset type"); 76 | break; 77 | } 78 | 79 | offset = zval_get_long(dim); 80 | } else { 81 | offset = Z_LVAL_P(dim); 82 | } 83 | 84 | return offset; 85 | } 86 | /* }}} */ 87 | 88 | static zval *php_xmark_fetch_dimension_address_inner(HashTable *ht, const zval *dim, int dim_type, int type) /* {{{ */ { 89 | zval *retval; 90 | zend_string *offset_key; 91 | zend_ulong hval; 92 | 93 | try_again: 94 | if (EXPECTED(Z_TYPE_P(dim) == IS_LONG)) { 95 | hval = Z_LVAL_P(dim); 96 | num_index: 97 | retval = zend_hash_index_find(ht, hval); 98 | if (retval == NULL) { 99 | switch (type) { 100 | case BP_VAR_R: 101 | zend_error(E_NOTICE,"Undefined offset: " ZEND_LONG_FMT, hval); 102 | /* break missing intentionally */ 103 | case BP_VAR_UNSET: 104 | case BP_VAR_IS: 105 | retval = &EG(uninitialized_zval); 106 | break; 107 | case BP_VAR_RW: 108 | zend_error(E_NOTICE,"Undefined offset: " ZEND_LONG_FMT, hval); 109 | /* break missing intentionally */ 110 | case BP_VAR_W: 111 | retval = zend_hash_index_add_new(ht, hval, &EG(uninitialized_zval)); 112 | break; 113 | } 114 | } 115 | } else if (EXPECTED(Z_TYPE_P(dim) == IS_STRING)) { 116 | offset_key = Z_STR_P(dim); 117 | if (dim_type != IS_CONST) { 118 | if (ZEND_HANDLE_NUMERIC(offset_key, hval)) { 119 | goto num_index; 120 | } 121 | } 122 | str_index: 123 | retval = zend_hash_find(ht, offset_key); 124 | if (retval) { 125 | /* support for $GLOBALS[...] */ 126 | if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT)) { 127 | retval = Z_INDIRECT_P(retval); 128 | if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) { 129 | switch (type) { 130 | case BP_VAR_R: 131 | zend_error(E_NOTICE, "Undefined index: %s", ZSTR_VAL(offset_key)); 132 | /* break missing intentionally */ 133 | case BP_VAR_UNSET: 134 | case BP_VAR_IS: 135 | retval = &EG(uninitialized_zval); 136 | break; 137 | case BP_VAR_RW: 138 | zend_error(E_NOTICE,"Undefined index: %s", ZSTR_VAL(offset_key)); 139 | /* break missing intentionally */ 140 | case BP_VAR_W: 141 | ZVAL_NULL(retval); 142 | break; 143 | } 144 | } 145 | } 146 | } else { 147 | switch (type) { 148 | case BP_VAR_R: 149 | zend_error(E_NOTICE, "Undefined index: %s", ZSTR_VAL(offset_key)); 150 | /* break missing intentionally */ 151 | case BP_VAR_UNSET: 152 | case BP_VAR_IS: 153 | retval = &EG(uninitialized_zval); 154 | break; 155 | case BP_VAR_RW: 156 | zend_error(E_NOTICE,"Undefined index: %s", ZSTR_VAL(offset_key)); 157 | /* break missing intentionally */ 158 | case BP_VAR_W: 159 | retval = zend_hash_add_new(ht, offset_key, &EG(uninitialized_zval)); 160 | break; 161 | } 162 | } 163 | } else { 164 | switch (Z_TYPE_P(dim)) { 165 | case IS_NULL: 166 | offset_key = ZSTR_EMPTY_ALLOC(); 167 | goto str_index; 168 | case IS_DOUBLE: 169 | hval = zend_dval_to_lval(Z_DVAL_P(dim)); 170 | goto num_index; 171 | case IS_RESOURCE: 172 | zend_error(E_NOTICE, "Resource ID#%pd used as offset, casting to integer (%pd)", Z_RES_HANDLE_P(dim), Z_RES_HANDLE_P(dim)); 173 | hval = Z_RES_HANDLE_P(dim); 174 | goto num_index; 175 | case IS_FALSE: 176 | hval = 0; 177 | goto num_index; 178 | case IS_TRUE: 179 | hval = 1; 180 | goto num_index; 181 | case IS_REFERENCE: 182 | dim = Z_REFVAL_P(dim); 183 | goto try_again; 184 | default: 185 | zend_error(E_WARNING, "Illegal offset type"); 186 | retval = (type == BP_VAR_W || type == BP_VAR_RW) ? 187 | #if PHP_VERSION_ID < 70100 188 | &EG(error_zval) 189 | #else 190 | NULL 191 | #endif 192 | : &EG(uninitialized_zval); 193 | } 194 | } 195 | return retval; 196 | } 197 | /* }}} */ 198 | 199 | static void php_xmark_fetch_dimension_address(zval *result, zval *container, zval *dim, int dim_type, int type) /* {{{ */ { 200 | zval *retval; 201 | 202 | if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { 203 | try_array: 204 | SEPARATE_ARRAY(container); 205 | fetch_from_array: 206 | if (dim == NULL) { 207 | retval = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval)); 208 | if (UNEXPECTED(retval == NULL)) { 209 | zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied"); 210 | #if PHP_VERSION_ID < 70100 211 | retval = &EG(error_zval); 212 | #else 213 | ZVAL_ERROR(result); 214 | return; 215 | #endif 216 | } 217 | } else { 218 | retval = php_xmark_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, dim_type, type); 219 | } 220 | ZVAL_INDIRECT(result, retval); 221 | return; 222 | } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) { 223 | container = Z_REFVAL_P(container); 224 | if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { 225 | goto try_array; 226 | } 227 | } 228 | if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { 229 | if (type != BP_VAR_UNSET && UNEXPECTED(Z_STRLEN_P(container) == 0)) { 230 | zval_ptr_dtor_nogc(container); 231 | convert_to_array: 232 | ZVAL_NEW_ARR(container); 233 | zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0); 234 | goto fetch_from_array; 235 | } 236 | 237 | if (dim == NULL) { 238 | zend_throw_error(NULL, "[] operator not supported for strings"); 239 | #if PHP_VERSION_ID < 70100 240 | ZVAL_INDIRECT(result, &EG(error_zval)); 241 | #else 242 | ZVAL_ERROR(result); 243 | #endif 244 | } else { 245 | php_xmark_check_string_offset(dim, type); 246 | #if PHP_VERSION_ID < 70100 247 | ZVAL_INDIRECT(result, NULL); /* wrong string offset */ 248 | #else 249 | ZVAL_ERROR(result); 250 | #endif 251 | } 252 | } else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { 253 | if (!Z_OBJ_HT_P(container)->read_dimension) { 254 | zend_throw_error(NULL, "Cannot use object as array"); 255 | #if PHP_VERSION_ID < 70100 256 | retval = &EG(error_zval); 257 | #else 258 | ZVAL_ERROR(result); 259 | #endif 260 | } else { 261 | retval = Z_OBJ_HT_P(container)->read_dimension(container, dim, type, result); 262 | 263 | if (UNEXPECTED(retval == &EG(uninitialized_zval))) { 264 | zend_class_entry *ce = Z_OBJCE_P(container); 265 | 266 | ZVAL_NULL(result); 267 | zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name)); 268 | } else if (EXPECTED(retval && Z_TYPE_P(retval) != IS_UNDEF)) { 269 | if (!Z_ISREF_P(retval)) { 270 | if (Z_REFCOUNTED_P(retval) && 271 | Z_REFCOUNT_P(retval) > 1) { 272 | if (Z_TYPE_P(retval) != IS_OBJECT) { 273 | Z_DELREF_P(retval); 274 | ZVAL_DUP(result, retval); 275 | retval = result; 276 | } else { 277 | ZVAL_COPY_VALUE(result, retval); 278 | retval = result; 279 | } 280 | } 281 | if (Z_TYPE_P(retval) != IS_OBJECT) { 282 | zend_class_entry *ce = Z_OBJCE_P(container); 283 | zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name)); 284 | } 285 | } else if (UNEXPECTED(Z_REFCOUNT_P(retval) == 1)) { 286 | ZVAL_UNREF(retval); 287 | } 288 | if (result != retval) { 289 | ZVAL_INDIRECT(result, retval); 290 | } 291 | } else { 292 | #if PHP_VERSION_ID < 70100 293 | ZVAL_INDIRECT(result, &EG(error_zval)); 294 | #else 295 | ZVAL_ERROR(result); 296 | #endif 297 | } 298 | } 299 | } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { 300 | if (UNEXPECTED(XMARK_ISERR(container))) { 301 | #if PHP_VERSION_ID < 70100 302 | ZVAL_INDIRECT(result, &EG(error_zval)); 303 | #else 304 | ZVAL_ERROR(result); 305 | #endif 306 | } else if (type != BP_VAR_UNSET) { 307 | goto convert_to_array; 308 | } else { 309 | /* for read-mode only */ 310 | ZVAL_NULL(result); 311 | } 312 | } else { 313 | if (type == BP_VAR_UNSET) { 314 | zend_error(E_WARNING, "Cannot unset offset in a non-array variable"); 315 | ZVAL_NULL(result); 316 | } else { 317 | zend_error(E_WARNING, "Cannot use a scalar value as an array"); 318 | #if PHP_VERSION_ID < 70100 319 | ZVAL_INDIRECT(result, &EG(error_zval)); 320 | #else 321 | ZVAL_ERROR(result); 322 | #endif 323 | } 324 | } 325 | } 326 | /* }}} */ 327 | 328 | static void php_xmark_assign_op_overloaded_property(zval *object, zval *property, void **cache_slot, zval *value, binary_op_type binary_op, zval *result) /* {{{ */ { 329 | zval *z; 330 | zval rv, obj; 331 | zval *zptr; 332 | zval *z_fname; 333 | zval call_func_ret, call_func_params[2]; 334 | 335 | ZVAL_OBJ(&obj, Z_OBJ_P(object)); 336 | Z_ADDREF(obj); 337 | if (Z_OBJ_HT(obj)->read_property && 338 | (z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv)) != NULL) { 339 | if (EG(exception)) { 340 | OBJ_RELEASE(Z_OBJ(obj)); 341 | return; 342 | } 343 | if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { 344 | zval rv2; 345 | zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); 346 | 347 | if (z == &rv) { 348 | zval_ptr_dtor(&rv); 349 | } 350 | ZVAL_COPY_VALUE(z, value); 351 | } 352 | zptr = z; 353 | ZVAL_DEREF(z); 354 | SEPARATE_ZVAL_NOREF(z); 355 | 356 | // only for ZEND_ASSIGN_CONCAT 357 | z_fname = zend_hash_index_find(&XMARK_G(callbacks), ZEND_ASSIGN_CONCAT); 358 | if (z_fname) { 359 | ZVAL_COPY_VALUE(&call_func_params[0], z); 360 | ZVAL_COPY_VALUE(&call_func_params[1], value); 361 | if (SUCCESS != call_user_function(EG(function_table), NULL, z_fname, &call_func_ret, 2, call_func_params)) { 362 | zend_error(E_WARNING, "call function error"); 363 | } 364 | zval_ptr_dtor_nogc(z); 365 | ZVAL_COPY_VALUE(z, &call_func_ret); 366 | } else { 367 | binary_op(z, z, value); 368 | } 369 | 370 | Z_OBJ_HT(obj)->write_property(&obj, property, z, cache_slot); 371 | if (result) { 372 | ZVAL_COPY(result, z); 373 | } 374 | 375 | zval_ptr_dtor(zptr); 376 | } else { 377 | zend_error(E_WARNING, "Attempt to assign property of non-object"); 378 | if (result) { 379 | ZVAL_NULL(result); 380 | } 381 | } 382 | OBJ_RELEASE(Z_OBJ(obj)); 383 | } 384 | /* }}} */ 385 | 386 | static void php_xmark_binary_assign_op_obj_dim(zval *object, zval *property, zval *value, zval *retval, binary_op_type binary_op) /* {{{ */ { 387 | zval *z; 388 | zval rv, res; 389 | zval *z_fname; 390 | zval call_func_ret, call_func_params[2]; 391 | 392 | if (Z_OBJ_HT_P(object)->read_dimension && 393 | (z = Z_OBJ_HT_P(object)->read_dimension(object, property, BP_VAR_R, &rv)) != NULL) { 394 | 395 | if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { 396 | zval rv2; 397 | zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); 398 | 399 | if (z == &rv) { 400 | zval_ptr_dtor(&rv); 401 | } 402 | ZVAL_COPY_VALUE(z, value); 403 | } 404 | 405 | // only for ZEND_ASSIGN_CONCAT 406 | z_fname = zend_hash_index_find(&XMARK_G(callbacks), ZEND_ASSIGN_CONCAT); 407 | if (z_fname) { 408 | ZVAL_COPY_VALUE(&call_func_params[0], Z_ISREF_P(z) ? Z_REFVAL_P(z) : z); 409 | ZVAL_COPY_VALUE(&call_func_params[1], value); 410 | if (SUCCESS != call_user_function(EG(function_table), NULL, z_fname, &call_func_ret, 2, call_func_params)) { 411 | zend_error(E_WARNING, "call function error"); 412 | } 413 | 414 | ZVAL_COPY_VALUE(&res, &call_func_ret); 415 | } else { 416 | binary_op(&res, Z_ISREF_P(z) ? Z_REFVAL_P(z) : z, value); 417 | } 418 | 419 | Z_OBJ_HT_P(object)->write_dimension(object, property, &res); 420 | if (z == &rv) { 421 | zval_ptr_dtor(&rv); 422 | } 423 | if (retval) { 424 | ZVAL_COPY(retval, &res); 425 | } 426 | 427 | zval_ptr_dtor(&res); 428 | } else { 429 | zend_error(E_WARNING, "Attempt to assign property of non-object"); 430 | if (retval) { 431 | ZVAL_NULL(retval); 432 | } 433 | } 434 | } 435 | /* }}} */ 436 | /* Copied codes end */ 437 | 438 | /* These are most copied from taint.c: zend_fetch_dimension_address */ 439 | 440 | static zval *php_xmark_get_zval_ptr_tmpvar(zend_execute_data *execute_data, uint32_t var, zend_free_op *should_free) /* {{{ */ { 441 | zval *ret = EX_VAR(var); 442 | 443 | if (should_free) { 444 | *should_free = ret; 445 | } 446 | ZVAL_DEREF(ret); 447 | 448 | return ret; 449 | } 450 | /* }}} */ 451 | 452 | #ifndef CV_DEF_OF 453 | #define CV_DEF_OF(i) (EX(func)->op_array.vars[i]) 454 | #endif 455 | 456 | static zval *php_xmark_get_zval_ptr_cv(zend_execute_data *execute_data, uint32_t var, int type, int force_ret) /* {{{ */ { 457 | zval *ret = EX_VAR(var); 458 | 459 | if (UNEXPECTED(Z_TYPE_P(ret) == IS_UNDEF)) { 460 | if (force_ret) { 461 | switch (type) { 462 | case BP_VAR_R: 463 | case BP_VAR_UNSET: 464 | zend_error(E_NOTICE, "Undefined variable: %s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(var)))); 465 | case BP_VAR_IS: 466 | ret = &EG(uninitialized_zval); 467 | break; 468 | case BP_VAR_RW: 469 | zend_error(E_NOTICE, "Undefined variable: %s", ZSTR_VAL(CV_DEF_OF(EX_VAR_TO_NUM(var)))); 470 | case BP_VAR_W: 471 | ZVAL_NULL(ret); 472 | break; 473 | } 474 | } else { 475 | return NULL; 476 | } 477 | } else { 478 | ZVAL_DEREF(ret); 479 | } 480 | return ret; 481 | } 482 | /* }}} */ 483 | 484 | 485 | static zval *php_xmark_get_zval_ptr(zend_execute_data *execute_data, int op_type, znode_op op, zend_free_op *should_free, int type, int force_ret) /* {{{ */ { 486 | if (op_type & (IS_TMP_VAR|IS_VAR)) { 487 | return php_xmark_get_zval_ptr_tmpvar(execute_data, op.var, should_free); 488 | } else { 489 | *should_free = NULL; 490 | if (op_type == IS_CONST) { 491 | return EX_CONSTANT(op); 492 | } else if (op_type == IS_CV) { 493 | return php_xmark_get_zval_ptr_cv(execute_data, op.var, type, force_ret); 494 | } else { 495 | return NULL; 496 | } 497 | } 498 | } 499 | /* }}} */ 500 | 501 | 502 | static zval *php_xmark_get_zval_ptr_ptr_var(zend_execute_data *execute_data, uint32_t var, zend_free_op *should_free) /* {{{ */ { 503 | zval *ret = EX_VAR(var); 504 | 505 | if (EXPECTED(Z_TYPE_P(ret) == IS_INDIRECT)) { 506 | *should_free = NULL; 507 | ret = Z_INDIRECT_P(ret); 508 | } else { 509 | *should_free = ret; 510 | } 511 | return ret; 512 | } 513 | /* }}} */ 514 | 515 | 516 | static zval *php_xmark_get_zval_ptr_ptr(zend_execute_data *execute_data, int op_type, znode_op op, zend_free_op *should_free, int type) /* {{{ */ { 517 | if (op_type == IS_CV) { 518 | *should_free = NULL; 519 | return php_xmark_get_zval_ptr_cv(execute_data, op.var, type, 1); 520 | } else if (op_type == IS_VAR) { 521 | ZEND_ASSERT(op_type == IS_VAR); 522 | return php_xmark_get_zval_ptr_ptr_var(execute_data, op.var, should_free); 523 | } else if (op_type == IS_UNUSED) { 524 | *should_free = NULL; 525 | return &EX(This); 526 | } else { 527 | ZEND_ASSERT(0); 528 | } 529 | } 530 | /* }}} */ 531 | 532 | 533 | static int php_xmark_binary_assign_op_helper(binary_op_type binary_op, zend_execute_data *execute_data) /* {{{ */ { 534 | const zend_op *opline = execute_data->opline; 535 | zval *var_ptr, *value; 536 | zend_free_op free_op1, free_op2; 537 | zval *z_fname; 538 | zval call_func_ret, call_func_params[2]; 539 | 540 | value = php_xmark_get_zval_ptr(execute_data, opline->op2_type, opline->op2, &free_op2, BP_VAR_R, 1); 541 | var_ptr = php_xmark_get_zval_ptr_ptr(execute_data, opline->op1_type, opline->op1, &free_op1, BP_VAR_RW); 542 | 543 | if (opline->op1_type == IS_VAR) { 544 | if (var_ptr == NULL || XMARK_ISERR(var_ptr)) { 545 | return ZEND_USER_OPCODE_DISPATCH; 546 | } 547 | } 548 | 549 | z_fname = zend_hash_index_find(&XMARK_G(callbacks), ZEND_ASSIGN_CONCAT); 550 | if (z_fname) { 551 | ZVAL_COPY_VALUE(&call_func_params[0], var_ptr); 552 | ZVAL_COPY_VALUE(&call_func_params[1], value); 553 | if (SUCCESS != call_user_function(EG(function_table), NULL, z_fname, &call_func_ret, 2, call_func_params)) { 554 | zend_error(E_WARNING, "call function error"); 555 | } 556 | 557 | SEPARATE_ZVAL_NOREF(var_ptr); 558 | zval_ptr_dtor_nogc(var_ptr); 559 | ZVAL_COPY_VALUE(var_ptr, &call_func_ret); 560 | } else { 561 | SEPARATE_ZVAL_NOREF(var_ptr); 562 | binary_op(var_ptr, var_ptr, value); 563 | 564 | } 565 | 566 | if (XMARK_RET_USED(opline)) { 567 | ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); 568 | } 569 | 570 | if ((XMARK_OP1_TYPE(opline) & (IS_VAR|IS_TMP_VAR)) && free_op1) { 571 | zval_ptr_dtor_nogc(free_op1); 572 | } 573 | 574 | if ((XMARK_OP2_TYPE(opline) & (IS_VAR|IS_TMP_VAR)) && free_op2) { 575 | zval_ptr_dtor_nogc(free_op2); 576 | } 577 | 578 | execute_data->opline++; 579 | 580 | return ZEND_USER_OPCODE_CONTINUE; 581 | } 582 | /* }}} */ 583 | 584 | 585 | static int php_xmark_binary_assign_op_obj_helper(binary_op_type binary_op, zend_execute_data *execute_data) /* {{{ */ { 586 | const zend_op *opline = execute_data->opline; 587 | zval *object, *property, *var_ptr, *value; 588 | zend_free_op free_op1, free_op2, free_op_data; 589 | zval *z_fname; 590 | zval call_func_ret, call_func_params[2]; 591 | 592 | object = php_xmark_get_zval_ptr_ptr(execute_data, opline->op1_type, opline->op1, &free_op1, BP_VAR_RW); 593 | if (opline->op1_type == IS_UNUSED && Z_OBJ_P(object) == NULL) { 594 | return ZEND_USER_OPCODE_DISPATCH; 595 | } 596 | if (opline->op1_type == IS_VAR && object == NULL) { 597 | return ZEND_USER_OPCODE_DISPATCH; 598 | } 599 | 600 | property = php_xmark_get_zval_ptr(execute_data, opline->op2_type, opline->op2, &free_op2, BP_VAR_R, 1); 601 | 602 | do { 603 | if (opline->op1_type == IS_UNUSED || Z_TYPE_P(object) != IS_OBJECT) { 604 | if (!php_xmark_make_real_object(object)) { 605 | zend_error(E_WARNING, "Attempt to assign property of non-object"); 606 | if (XMARK_RET_USED(opline)) { 607 | ZVAL_NULL(EX_VAR(opline->result.var)); 608 | } 609 | break; 610 | } 611 | } 612 | 613 | value = php_xmark_get_zval_ptr(execute_data, (opline + 1)->op1_type, (opline + 1)->op1, &free_op_data, BP_VAR_R, 1); 614 | 615 | if (Z_OBJ_HT_P(object)->get_property_ptr_ptr 616 | && (var_ptr = Z_OBJ_HT_P(object)->get_property_ptr_ptr(object, property, BP_VAR_RW, NULL)) != NULL) { 617 | ZVAL_DEREF(var_ptr); 618 | SEPARATE_ZVAL_NOREF(var_ptr); 619 | 620 | z_fname = zend_hash_index_find(&XMARK_G(callbacks), ZEND_ASSIGN_CONCAT); 621 | if (z_fname) { 622 | ZVAL_COPY_VALUE(&call_func_params[0], var_ptr); 623 | ZVAL_COPY_VALUE(&call_func_params[1], value); 624 | if (SUCCESS != call_user_function(EG(function_table), NULL, z_fname, &call_func_ret, 2, call_func_params)) { 625 | zend_error(E_WARNING, "call function error"); 626 | } 627 | 628 | zval_ptr_dtor_nogc(var_ptr); 629 | ZVAL_COPY_VALUE(var_ptr, &call_func_ret); 630 | } else { 631 | binary_op(var_ptr, var_ptr, value); 632 | } 633 | 634 | if (XMARK_RET_USED(opline)) { 635 | ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); 636 | } 637 | } else { 638 | php_xmark_assign_op_overloaded_property(object, property, NULL, value, binary_op, EX_VAR(opline->result.var)); 639 | if (!XMARK_RET_USED(opline)) { 640 | zval_ptr_dtor_nogc(EX_VAR(opline->result.var)); 641 | } 642 | } 643 | } while (0); 644 | 645 | if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && free_op2) { 646 | zval_ptr_dtor_nogc(free_op2); 647 | } 648 | if (((opline + 1)->op1_type & (IS_VAR|IS_TMP_VAR)) && free_op_data) { 649 | zval_ptr_dtor_nogc(free_op_data); 650 | } 651 | if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && free_op1) { 652 | zval_ptr_dtor_nogc(free_op1); 653 | } 654 | execute_data->opline += 2; 655 | 656 | return ZEND_USER_OPCODE_CONTINUE; 657 | } 658 | /* }}} */ 659 | 660 | 661 | static int php_xmark_binary_assign_op_dim_helper(binary_op_type binary_op, zend_execute_data *execute_data) /* {{{ */ { 662 | const zend_op *opline = execute_data->opline; 663 | zval *container, *dim, *var_ptr, *value, rv; 664 | zend_free_op free_op1, free_op2, free_op_data; 665 | zval *z_fname; 666 | zval call_func_ret, call_func_params[2]; 667 | 668 | container = php_xmark_get_zval_ptr_ptr(execute_data, opline->op1_type, opline->op1, &free_op1, BP_VAR_RW); 669 | if (opline->op1_type == IS_UNUSED && Z_OBJ_P(container) == NULL) { 670 | return ZEND_USER_OPCODE_DISPATCH; 671 | } 672 | if (opline->op1_type == IS_VAR && container == NULL) { 673 | return ZEND_USER_OPCODE_DISPATCH; 674 | } 675 | 676 | dim = php_xmark_get_zval_ptr(execute_data, opline->op2_type, opline->op2, &free_op2, BP_VAR_R, 1); 677 | 678 | do { 679 | if (opline->op1_type == IS_UNUSED || Z_TYPE_P(container) == IS_OBJECT) { 680 | value = php_xmark_get_zval_ptr(execute_data, (opline + 1)->op1_type, (opline + 1)->op1, &free_op_data, BP_VAR_R, 1); 681 | php_xmark_binary_assign_op_obj_dim(container, dim, value, EX_VAR(opline->result.var), binary_op); 682 | 683 | if (!XMARK_RET_USED(opline)) { 684 | zval_ptr_dtor_nogc(EX_VAR(opline->result.var)); 685 | } 686 | break; 687 | } 688 | 689 | php_xmark_fetch_dimension_address(&rv, container, dim, opline->op2_type, BP_VAR_RW); 690 | value = php_xmark_get_zval_ptr(execute_data, (opline + 1)->op1_type, (opline + 1)->op1, &free_op_data, BP_VAR_R, 1); 691 | 692 | if (Z_TYPE(rv) != IS_INDIRECT) { 693 | var_ptr = NULL; 694 | } else { 695 | var_ptr = Z_INDIRECT(rv); 696 | } 697 | 698 | if (var_ptr == NULL) { 699 | zend_throw_error(NULL, "Cannot use assign-op operators with overloaded objects nor string offsets"); 700 | if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && free_op2) { 701 | zval_ptr_dtor_nogc(free_op2); 702 | } 703 | if (((opline + 1)->op1_type & (IS_VAR|IS_TMP_VAR)) && free_op_data) { 704 | zval_ptr_dtor_nogc(free_op_data); 705 | } 706 | if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && free_op1) { 707 | zval_ptr_dtor_nogc(free_op1); 708 | } 709 | execute_data->opline += 2; 710 | return ZEND_USER_OPCODE_CONTINUE; 711 | } 712 | 713 | if (XMARK_ISERR(var_ptr)) { 714 | if (XMARK_RET_USED(opline)) { 715 | ZVAL_NULL(EX_VAR(opline->result.var)); 716 | } 717 | } else { 718 | ZVAL_DEREF(var_ptr); 719 | SEPARATE_ZVAL_NOREF(var_ptr); 720 | 721 | z_fname = zend_hash_index_find(&XMARK_G(callbacks), ZEND_ASSIGN_CONCAT); 722 | if (z_fname) { 723 | ZVAL_COPY_VALUE(&call_func_params[0], var_ptr); 724 | ZVAL_COPY_VALUE(&call_func_params[1], value); 725 | 726 | if (SUCCESS != call_user_function(EG(function_table), NULL, z_fname, &call_func_ret, 2, call_func_params)) { 727 | zend_error(E_WARNING, "call function error"); 728 | } 729 | zval_ptr_dtor_nogc(var_ptr); 730 | ZVAL_COPY_VALUE(var_ptr, &call_func_ret); 731 | } else { 732 | binary_op(var_ptr, var_ptr, value); 733 | } 734 | if (XMARK_RET_USED(opline)) { 735 | ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); 736 | } 737 | } 738 | } while (0); 739 | 740 | if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && free_op2) { 741 | zval_ptr_dtor_nogc(free_op2); 742 | } 743 | if (((opline + 1)->op1_type & (IS_VAR|IS_TMP_VAR)) && free_op_data) { 744 | zval_ptr_dtor_nogc(free_op_data); 745 | } 746 | if ((opline->op1_type & (IS_VAR|IS_TMP_VAR)) && free_op1) { 747 | zval_ptr_dtor_nogc(free_op1); 748 | } 749 | execute_data->opline += 2; 750 | 751 | return ZEND_USER_OPCODE_CONTINUE; 752 | } 753 | /* }}} */ 754 | /* Copied codes end */ 755 | 756 | 757 | static int php_xmark_op1_handler(zend_execute_data *execute_data) { 758 | const zend_op *opline = execute_data->opline; 759 | zend_free_op free_op1; 760 | zval *op1; 761 | zval *z_fname; 762 | zval call_func_ret; 763 | 764 | if (XMARK_G(in_callback)) { 765 | return ZEND_USER_OPCODE_DISPATCH; 766 | } 767 | 768 | z_fname = zend_hash_index_find(&XMARK_G(callbacks), opline->opcode); 769 | if (!z_fname) { 770 | return ZEND_USER_OPCODE_DISPATCH; 771 | } 772 | 773 | XMARK_G(in_callback) = 1; 774 | 775 | op1 = php_xmark_get_zval_ptr(execute_data, opline->op1_type, opline->op1, &free_op1, BP_VAR_R, 0); 776 | 777 | if (op1) { 778 | if (SUCCESS != call_user_function(EG(function_table), NULL, z_fname, &call_func_ret, 1, op1)) { 779 | zend_error(E_WARNING, "call function error"); 780 | } 781 | 782 | zval_ptr_dtor_nogc(&call_func_ret); 783 | } 784 | 785 | XMARK_G(in_callback) = 0; 786 | return ZEND_USER_OPCODE_DISPATCH; 787 | } 788 | 789 | 790 | static int php_xmark_op2_handler(zend_execute_data *execute_data) { 791 | const zend_op *opline = execute_data->opline; 792 | zend_free_op free_op2; 793 | zval *op2; 794 | zval *z_fname; 795 | zval call_func_ret; 796 | 797 | if (XMARK_G(in_callback)) { 798 | return ZEND_USER_OPCODE_DISPATCH; 799 | } 800 | 801 | z_fname = zend_hash_index_find(&XMARK_G(callbacks), opline->opcode); 802 | if (!z_fname) { 803 | return ZEND_USER_OPCODE_DISPATCH; 804 | } 805 | 806 | XMARK_G(in_callback) = 1; 807 | op2 = php_xmark_get_zval_ptr(execute_data, opline->op2_type, opline->op2, &free_op2, BP_VAR_R, 0); 808 | 809 | if (op2) { 810 | if (SUCCESS != call_user_function(EG(function_table), NULL, z_fname, &call_func_ret, 1, op2)) { 811 | zend_error(E_WARNING, "call function error"); 812 | } 813 | zval_ptr_dtor_nogc(&call_func_ret); 814 | } 815 | 816 | XMARK_G(in_callback) = 0; 817 | return ZEND_USER_OPCODE_DISPATCH; 818 | } 819 | 820 | 821 | static int php_xmark_concat_handler(zend_execute_data *execute_data) { 822 | const zend_op *opline = execute_data->opline; 823 | zval *op1, *op2, *result; 824 | zend_free_op free_op1, free_op2; 825 | zval *z_fname; 826 | zval call_func_ret, call_func_params[2]; 827 | 828 | if (XMARK_G(in_callback)) { 829 | return ZEND_USER_OPCODE_DISPATCH; 830 | } 831 | 832 | z_fname = zend_hash_index_find(&XMARK_G(callbacks), opline->opcode); 833 | if (!z_fname) { 834 | return ZEND_USER_OPCODE_DISPATCH; 835 | } 836 | 837 | XMARK_G(in_callback) = 1; 838 | 839 | op1 = php_xmark_get_zval_ptr(execute_data, opline->op1_type, opline->op1, &free_op1, BP_VAR_R, 1); 840 | op2 = php_xmark_get_zval_ptr(execute_data, opline->op2_type, opline->op2, &free_op2, BP_VAR_R, 1); 841 | 842 | result = EX_VAR(opline->result.var); 843 | 844 | if (op1 && op2) { 845 | ZVAL_COPY_VALUE(&call_func_params[0], op1); 846 | ZVAL_COPY_VALUE(&call_func_params[1], op2); 847 | if (SUCCESS != call_user_function(EG(function_table), NULL, z_fname, &call_func_ret, 2, call_func_params)) { 848 | zend_error(E_WARNING, "call function error"); 849 | } 850 | 851 | ZVAL_COPY_VALUE(result, &call_func_ret); 852 | } 853 | 854 | if ((XMARK_OP1_TYPE(opline) & (IS_VAR|IS_TMP_VAR)) && free_op1) { 855 | zval_ptr_dtor_nogc(free_op1); 856 | } 857 | 858 | if ((XMARK_OP2_TYPE(opline) & (IS_VAR|IS_TMP_VAR)) && free_op2) { 859 | zval_ptr_dtor_nogc(free_op2); 860 | } 861 | 862 | execute_data->opline++; 863 | XMARK_G(in_callback) = 0; 864 | 865 | return ZEND_USER_OPCODE_CONTINUE; 866 | } 867 | 868 | 869 | static int php_xmark_assign_concat_handler(zend_execute_data *execute_data) /* {{{ */ { 870 | const zend_op *opline = execute_data->opline; 871 | int result = 0; 872 | 873 | if (XMARK_G(in_callback)) { 874 | return ZEND_USER_OPCODE_DISPATCH; 875 | } 876 | 877 | XMARK_G(in_callback) = 1; 878 | 879 | if (EXPECTED(opline->extended_value == 0)) { 880 | result = php_xmark_binary_assign_op_helper(concat_function, execute_data); 881 | } else if (EXPECTED(opline->extended_value == ZEND_ASSIGN_DIM)) { 882 | result = php_xmark_binary_assign_op_dim_helper(concat_function, execute_data); 883 | } else { 884 | result = php_xmark_binary_assign_op_obj_helper(concat_function, execute_data); 885 | } 886 | 887 | XMARK_G(in_callback) = 0; 888 | return result; 889 | } /* }}} */ 890 | 891 | 892 | static int php_xmark_rope_end_handler(zend_execute_data *execute_data) { 893 | const zend_op *opline = execute_data->opline; 894 | zval *op2, *result; 895 | zend_free_op free_op2; 896 | zend_string **rope; 897 | int i; 898 | zval *z_fname; 899 | zval call_func_ret, call_func_params[1]; 900 | zval z_rope; 901 | 902 | if (XMARK_G(in_callback)) { 903 | return ZEND_USER_OPCODE_DISPATCH; 904 | } 905 | 906 | z_fname = zend_hash_index_find(&XMARK_G(callbacks), opline->opcode); 907 | if (!z_fname) { 908 | return ZEND_USER_OPCODE_DISPATCH; 909 | } 910 | 911 | XMARK_G(in_callback) = 1; 912 | 913 | rope = (zend_string **)EX_VAR(opline->op1.var); 914 | op2 = php_xmark_get_zval_ptr(execute_data, opline->op2_type, opline->op2, &free_op2, BP_VAR_R, 1); 915 | result = EX_VAR(opline->result.var); 916 | 917 | ZVAL_NEW_ARR(&z_rope); 918 | zend_hash_init(Z_ARRVAL(z_rope), opline->extended_value+1, NULL, ZVAL_PTR_DTOR, 0); 919 | 920 | rope[opline->extended_value] = zval_get_string(op2); 921 | 922 | zval tmp; 923 | for (i=0; i<=opline->extended_value; i++) { 924 | ZVAL_STR(&tmp, rope[i]); 925 | zend_hash_next_index_insert(Z_ARRVAL(z_rope), &tmp); 926 | } 927 | 928 | ZVAL_COPY_VALUE(&call_func_params[0], &z_rope); 929 | 930 | if (SUCCESS != call_user_function(EG(function_table), NULL, z_fname, &call_func_ret, 1, call_func_params)) { 931 | zend_error(E_WARNING, "call function error"); 932 | } 933 | 934 | if (Z_REFCOUNT(z_rope) <= 1) 935 | zend_array_destroy(Z_ARRVAL(z_rope)); 936 | else 937 | Z_DELREF(z_rope); 938 | 939 | ZVAL_COPY_VALUE(result, &call_func_ret); 940 | execute_data->opline++; 941 | XMARK_G(in_callback) = 0; 942 | 943 | return ZEND_USER_OPCODE_CONTINUE; 944 | } 945 | 946 | 947 | static int php_xmark_fcall_handler(zend_execute_data *execute_data) { 948 | const zend_op *opline = execute_data->opline; 949 | zend_execute_data *call = execute_data->call; 950 | zend_function *fbc = call->func; 951 | zval *z_fname; 952 | zval call_func_ret, call_func_params[2]; 953 | zval z_call, z_params, tmp; 954 | zend_string *fname, *cname; 955 | uint32_t i; 956 | 957 | ZEND_CALL_ARG(call, 1); 958 | 959 | if (XMARK_G(in_callback)) { 960 | return ZEND_USER_OPCODE_DISPATCH; 961 | } 962 | 963 | z_fname = zend_hash_index_find(&XMARK_G(callbacks), opline->opcode); 964 | if (!z_fname) { 965 | return ZEND_USER_OPCODE_DISPATCH; 966 | } 967 | 968 | if (!fbc->common.function_name) { 969 | return ZEND_USER_OPCODE_DISPATCH; 970 | } 971 | 972 | XMARK_G(in_callback) = 1; 973 | 974 | uint32_t arg_count = ZEND_CALL_NUM_ARGS(call); 975 | 976 | ZVAL_NEW_ARR(&z_params); 977 | zend_hash_init(Z_ARRVAL(z_params), arg_count, NULL, ZVAL_PTR_DTOR, 0); 978 | 979 | for (i=0; icommon.scope == NULL) { 988 | fname = fbc->common.function_name; 989 | ZVAL_STR_COPY(&z_call, fname); 990 | ZVAL_COPY_VALUE(&call_func_params[0], &z_call); 991 | } else { 992 | cname = fbc->common.scope->name; 993 | fname = fbc->common.function_name; 994 | 995 | ZVAL_NEW_ARR(&z_call); 996 | zend_hash_init(Z_ARRVAL(z_call), 2, NULL, ZVAL_PTR_DTOR, 0); 997 | 998 | ZVAL_STR_COPY(&tmp, cname); 999 | zend_hash_next_index_insert(Z_ARRVAL(z_call), &tmp); 1000 | ZVAL_STR_COPY(&tmp, fname); 1001 | zend_hash_next_index_insert(Z_ARRVAL(z_call), &tmp); 1002 | 1003 | ZVAL_COPY_VALUE(&call_func_params[0], &z_call); 1004 | } 1005 | 1006 | if (SUCCESS != call_user_function(EG(function_table), NULL, z_fname, &call_func_ret, 2, call_func_params)) { 1007 | zend_error(E_WARNING, "call function error"); 1008 | } 1009 | 1010 | if (IS_ARRAY == Z_TYPE_P(&z_call)) { 1011 | if (Z_REFCOUNT(z_call) <= 1) 1012 | zend_array_destroy(Z_ARRVAL(z_call)); 1013 | else 1014 | Z_DELREF(z_call); 1015 | } else { 1016 | zend_string_release(Z_STR(z_call)); 1017 | } 1018 | 1019 | if (Z_REFCOUNT(z_params) <= 1) 1020 | zend_array_destroy(Z_ARRVAL(z_params)); 1021 | else 1022 | Z_DELREF(z_params); 1023 | 1024 | 1025 | XMARK_G(in_callback) = 0; 1026 | 1027 | return ZEND_USER_OPCODE_DISPATCH; 1028 | } 1029 | 1030 | 1031 | static int php_xmark_init_fcall(zend_execute_data *execute_data) { 1032 | // 重新设置 stack size 1033 | zend_op *opline = (zend_op *)execute_data->opline; 1034 | 1035 | zval *fname = EX_CONSTANT(opline->op2); 1036 | zval *func; 1037 | zend_function *fbc; 1038 | 1039 | func = zend_hash_find(EG(function_table), Z_STR_P(fname)); 1040 | if (UNEXPECTED(func == NULL)) { 1041 | return ZEND_USER_OPCODE_DISPATCH; 1042 | } 1043 | fbc = Z_FUNC_P(func); 1044 | 1045 | if (UNEXPECTED(fbc->type != ZEND_USER_FUNCTION)) { 1046 | return ZEND_USER_OPCODE_DISPATCH; 1047 | } 1048 | 1049 | opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, fbc); 1050 | return ZEND_USER_OPCODE_DISPATCH; 1051 | } 1052 | 1053 | 1054 | static void php_xmark_register_opcode_handlers() 1055 | { 1056 | zend_set_user_opcode_handler(ZEND_ECHO, php_xmark_op1_handler); 1057 | zend_set_user_opcode_handler(ZEND_EXIT, php_xmark_op1_handler); 1058 | zend_set_user_opcode_handler(ZEND_INIT_METHOD_CALL, php_xmark_op2_handler); 1059 | zend_set_user_opcode_handler(ZEND_INIT_USER_CALL, php_xmark_op2_handler); 1060 | zend_set_user_opcode_handler(ZEND_INIT_DYNAMIC_CALL, php_xmark_op2_handler); 1061 | zend_set_user_opcode_handler(ZEND_INCLUDE_OR_EVAL, php_xmark_op1_handler); 1062 | zend_set_user_opcode_handler(ZEND_CONCAT, php_xmark_concat_handler); 1063 | zend_set_user_opcode_handler(ZEND_FAST_CONCAT, php_xmark_concat_handler); 1064 | zend_set_user_opcode_handler(ZEND_ASSIGN_CONCAT, php_xmark_assign_concat_handler); 1065 | zend_set_user_opcode_handler(ZEND_ROPE_END, php_xmark_rope_end_handler); 1066 | zend_set_user_opcode_handler(ZEND_DO_FCALL, php_xmark_fcall_handler); 1067 | zend_set_user_opcode_handler(ZEND_DO_ICALL, php_xmark_fcall_handler); 1068 | zend_set_user_opcode_handler(ZEND_DO_UCALL, php_xmark_fcall_handler); 1069 | zend_set_user_opcode_handler(ZEND_DO_FCALL_BY_NAME, php_xmark_fcall_handler); 1070 | 1071 | if (XMARK_G(enable_rename)) 1072 | zend_set_user_opcode_handler(ZEND_INIT_FCALL, php_xmark_init_fcall); 1073 | } 1074 | 1075 | 1076 | static void rename_from_ini_value(HashTable *ht, const char *ini_value, int type) 1077 | { 1078 | char *e, *orig_name = NULL, *new_name = NULL; 1079 | 1080 | if (!ini_value) { 1081 | return; 1082 | } 1083 | 1084 | e = strdup(ini_value); 1085 | if (e == NULL) { 1086 | return; 1087 | } 1088 | 1089 | while (*e) { 1090 | switch (*e) { 1091 | case ' ': 1092 | case '\r': 1093 | case '\n': 1094 | case '\t': 1095 | case ',': 1096 | if (orig_name && new_name) { 1097 | *e = '\0'; 1098 | rename_hash_str_key(ht, orig_name, new_name, type); 1099 | } 1100 | orig_name = NULL; 1101 | new_name = NULL; 1102 | break; 1103 | case ':': 1104 | if (orig_name) { 1105 | *e = '\0'; 1106 | } 1107 | if (!new_name) { 1108 | new_name = e + 1; 1109 | } 1110 | break; 1111 | default: 1112 | if (!orig_name) { 1113 | orig_name = e; 1114 | } 1115 | break; 1116 | } 1117 | e++; 1118 | } 1119 | if (orig_name && new_name) { 1120 | rename_hash_str_key(ht, orig_name, new_name, type); 1121 | } 1122 | } 1123 | 1124 | static zend_always_inline int xmark_zstr(zval *z_str) 1125 | { 1126 | if (!XCHECK_FLAG(Z_STR_P(z_str))) { 1127 | zend_string *str = zend_string_init(Z_STRVAL_P(z_str), Z_STRLEN_P(z_str), 0); 1128 | ZSTR_LEN(str) = Z_STRLEN_P(z_str); 1129 | zend_string_release(Z_STR_P(z_str)); 1130 | XMARK_FLAG(str); 1131 | ZVAL_STR(z_str, str); 1132 | } 1133 | 1134 | return SUCCESS; 1135 | } 1136 | 1137 | 1138 | static zend_always_inline Bucket *rename_hash_key(HashTable *ht, zend_string *orig_name, zend_string *new_name, int type) 1139 | { 1140 | zend_ulong h; 1141 | uint32_t nIndex; 1142 | uint32_t idx; 1143 | Bucket *p = NULL, *arData, *prev = NULL; 1144 | zend_bool found = 0; 1145 | 1146 | orig_name = zend_string_tolower(orig_name); 1147 | new_name = zend_string_tolower(new_name); 1148 | 1149 | if (zend_hash_exists(ht, new_name)) { 1150 | zend_string_release(orig_name); 1151 | zend_string_release(new_name); 1152 | zend_error(E_ERROR, "function/class '%s' already exists", ZSTR_VAL(new_name)); 1153 | return NULL; 1154 | } 1155 | 1156 | h = zend_string_hash_val(orig_name); 1157 | arData = ht->arData; 1158 | nIndex = h | ht->nTableMask; 1159 | idx = HT_HASH_EX(arData, nIndex); 1160 | while (EXPECTED(idx != HT_INVALID_IDX)) { 1161 | prev = p; 1162 | p = HT_HASH_TO_BUCKET_EX(arData, idx); 1163 | if (EXPECTED(p->key == orig_name)) { /* check for the same interned string */ 1164 | found = 1; 1165 | break; 1166 | } else if (EXPECTED(p->h == h) && 1167 | EXPECTED(p->key) && 1168 | EXPECTED(ZSTR_LEN(p->key) == ZSTR_LEN(orig_name)) && 1169 | EXPECTED(memcmp(ZSTR_VAL(p->key), ZSTR_VAL(orig_name), ZSTR_LEN(orig_name)) == 0)) { 1170 | found = 1; 1171 | break; 1172 | } 1173 | idx = Z_NEXT(p->val); 1174 | } 1175 | 1176 | if (!found) { 1177 | zend_string_release(orig_name); 1178 | zend_string_release(new_name); 1179 | zend_error(E_ERROR, "function/class '%s' does not exists", ZSTR_VAL(orig_name)); 1180 | return NULL; 1181 | } 1182 | 1183 | // rehash 1184 | if (!prev && Z_NEXT(p->val) == HT_INVALID_IDX) { // only p 1185 | HT_HASH(ht, nIndex) = HT_INVALID_IDX; 1186 | } else if (prev && Z_NEXT(p->val) != HT_INVALID_IDX) { // p in middle 1187 | Z_NEXT(prev->val) = Z_NEXT(p->val); 1188 | } else if (prev && Z_NEXT(p->val) == HT_INVALID_IDX) { // p in tail 1189 | Z_NEXT(prev->val) = HT_INVALID_IDX; 1190 | } else if (!prev && Z_NEXT(p->val) != HT_INVALID_IDX) { // p in head 1191 | HT_HASH(ht, nIndex) = Z_NEXT(p->val); 1192 | } 1193 | 1194 | zend_string_release(p->key); 1195 | p->key = zend_string_init_interned(ZSTR_VAL(new_name), ZSTR_LEN(new_name), 1); 1196 | p->h = h = zend_string_hash_val(p->key); 1197 | nIndex = h | ht->nTableMask; 1198 | 1199 | // 重命名函数名 1200 | if (type == XMARK_IS_FUNCTION) { 1201 | zend_string_release(p->val.value.func->common.function_name); 1202 | zend_string_addref(p->key); 1203 | p->val.value.func->common.function_name = p->key; 1204 | } 1205 | /* 1206 | else if (type == XMARK_IS_CLASS) { 1207 | zend_string_release(p->val.value.ce->name); 1208 | zend_string_addref(p->key); 1209 | p->val.value.ce->name = p->key; 1210 | } 1211 | */ 1212 | 1213 | if (HT_HASH(ht, nIndex) != HT_INVALID_IDX) 1214 | Z_NEXT(p->val) = HT_HASH(ht, nIndex); 1215 | 1216 | // 这里没必要再继续使用 HT_IDX_TO_HASH, 因为我们直接从 ht 中拿到的 idx, 不然 x86 下会报错 1217 | HT_HASH(ht, nIndex) = idx; 1218 | 1219 | zend_string_release(orig_name); 1220 | zend_string_release(new_name); 1221 | 1222 | return p; 1223 | } 1224 | 1225 | 1226 | static zend_always_inline Bucket *rename_hash_str_key(HashTable *ht, const char *orig_name, const char *new_name, int type) 1227 | { 1228 | zend_string *str_orig_name, *str_new_name; 1229 | Bucket *p; 1230 | 1231 | str_orig_name = zend_string_init(orig_name, strlen(orig_name), 0); 1232 | str_new_name = zend_string_init(new_name, strlen(new_name), 0); 1233 | 1234 | p = rename_hash_key(ht, str_orig_name, str_new_name, type); 1235 | 1236 | zend_string_release(str_orig_name); 1237 | zend_string_release(str_new_name); 1238 | 1239 | return p; 1240 | } 1241 | 1242 | 1243 | static void clear_function_run_time_cache(zend_function *fbc) 1244 | { 1245 | if (fbc->type != ZEND_USER_FUNCTION || 1246 | fbc->op_array.cache_size == 0 || fbc->op_array.run_time_cache == NULL) return; 1247 | 1248 | memset(fbc->op_array.run_time_cache, 0, fbc->op_array.cache_size); 1249 | } 1250 | 1251 | static void clear_run_time_cache() 1252 | { 1253 | zend_function *fbc; 1254 | zend_class_entry *ce; 1255 | 1256 | ZEND_HASH_FOREACH_PTR(EG(function_table), fbc) { 1257 | clear_function_run_time_cache(fbc); 1258 | } ZEND_HASH_FOREACH_END(); 1259 | 1260 | 1261 | ZEND_HASH_FOREACH_PTR(EG(class_table), ce) { 1262 | ZEND_HASH_FOREACH_PTR(&(ce->function_table), fbc) { 1263 | clear_function_run_time_cache(fbc); 1264 | } ZEND_HASH_FOREACH_END(); 1265 | } ZEND_HASH_FOREACH_END(); 1266 | } 1267 | 1268 | 1269 | /* {{{ PHP_INI 1270 | */ 1271 | PHP_INI_BEGIN() 1272 | STD_PHP_INI_BOOLEAN("xmark.enable", "0", PHP_INI_SYSTEM, OnUpdateBool, enable, zend_xmark_globals, xmark_globals) 1273 | STD_PHP_INI_BOOLEAN("xmark.enable_rename", "0", PHP_INI_SYSTEM, OnUpdateBool, enable_rename, zend_xmark_globals, xmark_globals) 1274 | STD_PHP_INI_ENTRY("xmark.rename_functions", "", PHP_INI_SYSTEM, OnUpdateString, rename_functions, zend_xmark_globals, xmark_globals) 1275 | STD_PHP_INI_ENTRY("xmark.rename_classes", "", PHP_INI_SYSTEM, OnUpdateString, rename_classes, zend_xmark_globals, xmark_globals) 1276 | PHP_INI_END() 1277 | /* }}} */ 1278 | 1279 | PHP_FUNCTION(xid) 1280 | { 1281 | zval *zv; 1282 | 1283 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &zv) == FAILURE) { 1284 | return; 1285 | } 1286 | 1287 | RETURN_LONG((zend_long)zv->value.ptr); 1288 | } 1289 | 1290 | 1291 | PHP_FUNCTION(xmark) 1292 | { 1293 | zval *z_str; 1294 | 1295 | if (!XMARK_G(enable)) { 1296 | RETURN_FALSE; 1297 | } 1298 | 1299 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &z_str) == FAILURE) { 1300 | return; 1301 | } 1302 | 1303 | ZVAL_DEREF(z_str); 1304 | if (IS_STRING != Z_TYPE_P(z_str) || Z_STRLEN_P(z_str) == 0) { 1305 | RETURN_FALSE; 1306 | } 1307 | 1308 | if (xmark_zstr(z_str) == FAILURE) { 1309 | RETURN_FALSE; 1310 | } 1311 | 1312 | RETURN_TRUE; 1313 | } 1314 | 1315 | 1316 | PHP_FUNCTION(xclear) 1317 | { 1318 | zval *z_str; 1319 | 1320 | if (!XMARK_G(enable)) { 1321 | RETURN_FALSE; 1322 | } 1323 | 1324 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &z_str) == FAILURE) { 1325 | return; 1326 | } 1327 | 1328 | ZVAL_DEREF(z_str); 1329 | if (IS_STRING != Z_TYPE_P(z_str) || Z_STRLEN_P(z_str) == 0 || !XCHECK_FLAG(Z_STR_P(z_str))) { 1330 | RETURN_FALSE; 1331 | } 1332 | 1333 | XCLEAR_FLAG(Z_STR_P(z_str)); 1334 | RETURN_TRUE; 1335 | } 1336 | 1337 | 1338 | PHP_FUNCTION(xcheck) 1339 | { 1340 | zval *z_str; 1341 | 1342 | if (!XMARK_G(enable)) { 1343 | RETURN_FALSE; 1344 | } 1345 | 1346 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &z_str) == FAILURE) { 1347 | return; 1348 | } 1349 | 1350 | ZVAL_DEREF(z_str); 1351 | if (IS_STRING != Z_TYPE_P(z_str) || Z_STRLEN_P(z_str) == 0 || !XCHECK_FLAG(Z_STR_P(z_str))) { 1352 | RETURN_FALSE; 1353 | } 1354 | 1355 | RETURN_TRUE; 1356 | } 1357 | 1358 | 1359 | /* 1360 | * xrename_function('phpinfo', 'myphpinfo') 1361 | */ 1362 | PHP_FUNCTION(xrename_function) 1363 | { 1364 | zend_string *orig_fname, *new_fname, *lc_orig_fname; 1365 | zval *z_func; 1366 | 1367 | if (!XMARK_G(enable) || !XMARK_G(enable_rename)) { 1368 | RETURN_FALSE; 1369 | } 1370 | 1371 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &orig_fname, &new_fname) == FAILURE) { 1372 | return; 1373 | } 1374 | 1375 | lc_orig_fname = zend_string_tolower(orig_fname); 1376 | z_func = zend_hash_find(EG(function_table), lc_orig_fname); 1377 | zend_string_release(lc_orig_fname); 1378 | if (!z_func) { 1379 | zend_error(E_ERROR, "function '%s' does not exists", ZSTR_VAL(orig_fname)); 1380 | return; 1381 | } 1382 | 1383 | if (Z_FUNC_P(z_func)->type != ZEND_USER_FUNCTION) { 1384 | zend_error(E_ERROR, "xrename_function can only rename user function"); 1385 | return; 1386 | } 1387 | 1388 | Bucket *p = rename_hash_key(EG(function_table), orig_fname, new_fname, XMARK_IS_FUNCTION); 1389 | if (!p) { 1390 | zend_error(E_ERROR, "rename function '%s' to '%s' failed", ZSTR_VAL(orig_fname), ZSTR_VAL(new_fname)); 1391 | RETURN_FALSE; 1392 | } 1393 | 1394 | zend_string_release(Z_FUNC(p->val)->common.function_name); 1395 | Z_FUNC(p->val)->common.function_name = zend_string_init_interned(ZSTR_VAL(new_fname), ZSTR_LEN(new_fname), 1); 1396 | 1397 | clear_run_time_cache(); 1398 | 1399 | RETURN_TRUE; 1400 | } 1401 | 1402 | 1403 | PHP_FUNCTION(xrename_class) 1404 | { 1405 | zend_string *orig_cname, *new_cname, *lc_orig_cname; 1406 | zval *z_class; 1407 | 1408 | if (!XMARK_G(enable) || !XMARK_G(enable_rename)) { 1409 | RETURN_FALSE; 1410 | } 1411 | 1412 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &orig_cname, &new_cname) == FAILURE) { 1413 | return; 1414 | } 1415 | 1416 | lc_orig_cname = zend_string_tolower(orig_cname); 1417 | z_class = zend_hash_find(EG(class_table), lc_orig_cname); 1418 | zend_string_release(lc_orig_cname); 1419 | if (!z_class) { 1420 | zend_error(E_ERROR, "class '%s' does not exists", ZSTR_VAL(orig_cname)); 1421 | return; 1422 | } 1423 | 1424 | if (Z_CE_P(z_class)->type != ZEND_USER_CLASS) { 1425 | zend_error(E_ERROR, "xrename_class can only rename user class"); 1426 | return; 1427 | } 1428 | 1429 | Bucket *p = rename_hash_key(EG(class_table), orig_cname, new_cname, XMARK_IS_CLASS); 1430 | if (!p) { 1431 | zend_error(E_ERROR, "rename class '%s' to '%s' failed", ZSTR_VAL(orig_cname), ZSTR_VAL(new_cname)); 1432 | RETURN_FALSE; 1433 | } 1434 | 1435 | zend_string_release(Z_CE(p->val)->name); 1436 | Z_CE(p->val)->name = zend_string_init_interned(ZSTR_VAL(new_cname), ZSTR_LEN(new_cname), 1); 1437 | 1438 | RETURN_TRUE; 1439 | } 1440 | 1441 | 1442 | PHP_FUNCTION(xregister_opcode_callback) 1443 | { 1444 | zend_fcall_info callable; 1445 | zend_fcall_info_cache call_cache; 1446 | zend_ulong opcode; 1447 | 1448 | if (!XMARK_G(enable)) { 1449 | RETURN_FALSE; 1450 | } 1451 | 1452 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "lf", &opcode, &callable, &call_cache) == FAILURE) { 1453 | RETURN_FALSE; 1454 | } 1455 | 1456 | zend_string_addref(Z_STR(callable.function_name)); 1457 | if (!zend_hash_index_update(&XMARK_G(callbacks), opcode, &callable.function_name)) { 1458 | RETURN_FALSE; 1459 | } 1460 | 1461 | RETURN_TRUE; 1462 | } 1463 | 1464 | 1465 | 1466 | /* {{{ PHP_MINIT_FUNCTION 1467 | */ 1468 | PHP_MINIT_FUNCTION(xmark) 1469 | { 1470 | REGISTER_INI_ENTRIES(); 1471 | 1472 | if (!XMARK_G(enable)) { 1473 | return SUCCESS; 1474 | } 1475 | 1476 | REGISTER_LONG_CONSTANT("XMARK_ECHO", ZEND_ECHO, CONST_CS|CONST_PERSISTENT); 1477 | REGISTER_LONG_CONSTANT("XMARK_EXIT", ZEND_EXIT, CONST_CS|CONST_PERSISTENT); 1478 | REGISTER_LONG_CONSTANT("XMARK_INIT_METHOD_CALL", ZEND_INIT_METHOD_CALL, CONST_CS|CONST_PERSISTENT); 1479 | REGISTER_LONG_CONSTANT("XMARK_INIT_USER_CALL", ZEND_INIT_USER_CALL, CONST_CS|CONST_PERSISTENT); 1480 | REGISTER_LONG_CONSTANT("XMARK_INIT_DYNAMIC_CALL", ZEND_INIT_DYNAMIC_CALL, CONST_CS|CONST_PERSISTENT); 1481 | REGISTER_LONG_CONSTANT("XMARK_INCLUDE_OR_EVAL", ZEND_INCLUDE_OR_EVAL, CONST_CS|CONST_PERSISTENT); 1482 | REGISTER_LONG_CONSTANT("XMARK_CONCAT", ZEND_CONCAT, CONST_CS|CONST_PERSISTENT); 1483 | REGISTER_LONG_CONSTANT("XMARK_FAST_CONCAT", ZEND_FAST_CONCAT, CONST_CS|CONST_PERSISTENT); 1484 | REGISTER_LONG_CONSTANT("XMARK_ASSIGN_CONCAT", ZEND_ASSIGN_CONCAT, CONST_CS|CONST_PERSISTENT); 1485 | REGISTER_LONG_CONSTANT("XMARK_ROPE_END", ZEND_ROPE_END, CONST_CS|CONST_PERSISTENT); 1486 | REGISTER_LONG_CONSTANT("XMARK_DO_FCALL", ZEND_DO_FCALL, CONST_CS|CONST_PERSISTENT); 1487 | REGISTER_LONG_CONSTANT("XMARK_DO_ICALL", ZEND_DO_ICALL, CONST_CS|CONST_PERSISTENT); 1488 | REGISTER_LONG_CONSTANT("XMARK_DO_UCALL", ZEND_DO_UCALL, CONST_CS|CONST_PERSISTENT); 1489 | REGISTER_LONG_CONSTANT("XMARK_DO_FCALL_BY_NAME", ZEND_DO_FCALL_BY_NAME, CONST_CS|CONST_PERSISTENT); 1490 | 1491 | php_xmark_register_opcode_handlers(); 1492 | rename_from_ini_value(CG(function_table), XMARK_G(rename_functions), XMARK_IS_FUNCTION); 1493 | rename_from_ini_value(CG(class_table), XMARK_G(rename_classes), XMARK_IS_CLASS); 1494 | 1495 | return SUCCESS; 1496 | } 1497 | /* }}} */ 1498 | 1499 | 1500 | /* {{{ PHP_MSHUTDOWN_FUNCTION 1501 | */ 1502 | PHP_MSHUTDOWN_FUNCTION(xmark) 1503 | { 1504 | UNREGISTER_INI_ENTRIES(); 1505 | return SUCCESS; 1506 | } 1507 | /* }}} */ 1508 | 1509 | /* {{{ PHP_RINIT_FUNCTION 1510 | */ 1511 | PHP_RINIT_FUNCTION(xmark) 1512 | { 1513 | if (XMARK_G(enable)) { 1514 | XMARK_G(in_callback) = 0; 1515 | zend_hash_init(&XMARK_G(callbacks), 1, NULL, ZVAL_PTR_DTOR, 0); 1516 | } 1517 | 1518 | #if defined(COMPILE_DL_XMARK) && defined(ZTS) 1519 | ZEND_TSRMLS_CACHE_UPDATE(); 1520 | #endif 1521 | return SUCCESS; 1522 | } 1523 | /* }}} */ 1524 | 1525 | /* {{{ PHP_RSHUTDOWN_FUNCTION 1526 | */ 1527 | PHP_RSHUTDOWN_FUNCTION(xmark) 1528 | { 1529 | if (XMARK_G(enable)) { 1530 | zend_hash_destroy(&XMARK_G(callbacks)); 1531 | } 1532 | 1533 | return SUCCESS; 1534 | } 1535 | /* }}} */ 1536 | 1537 | /* {{{ PHP_MINFO_FUNCTION 1538 | */ 1539 | PHP_MINFO_FUNCTION(xmark) 1540 | { 1541 | php_info_print_table_start(); 1542 | php_info_print_table_header(2, "xmark support", "enabled"); 1543 | php_info_print_table_end(); 1544 | 1545 | DISPLAY_INI_ENTRIES(); 1546 | } 1547 | /* }}} */ 1548 | 1549 | ZEND_BEGIN_ARG_INFO_EX(xid_arginfo, 0, 0, 1) 1550 | ZEND_ARG_INFO(1, z) 1551 | ZEND_END_ARG_INFO() 1552 | 1553 | ZEND_BEGIN_ARG_INFO_EX(xmark_arginfo, 0, 0, 1) 1554 | ZEND_ARG_INFO(1, string) 1555 | ZEND_END_ARG_INFO() 1556 | 1557 | ZEND_BEGIN_ARG_INFO_EX(xclear_arginfo, 0, 0, 1) 1558 | ZEND_ARG_INFO(1, string) 1559 | ZEND_END_ARG_INFO() 1560 | 1561 | ZEND_BEGIN_ARG_INFO_EX(xcheck_arginfo, 0, 0, 1) 1562 | ZEND_ARG_INFO(0, string) 1563 | ZEND_END_ARG_INFO() 1564 | 1565 | /* {{{ xmark_functions[] 1566 | * 1567 | * Every user visible function must have an entry in xmark_functions[]. 1568 | */ 1569 | const zend_function_entry xmark_functions[] = { 1570 | PHP_FE(xid, xid_arginfo) 1571 | PHP_FE(xmark, xmark_arginfo) 1572 | PHP_FE(xclear, xclear_arginfo) 1573 | PHP_FE(xcheck, xcheck_arginfo) 1574 | PHP_FE(xrename_function, NULL) 1575 | PHP_FE(xrename_class, NULL) 1576 | PHP_FE(xregister_opcode_callback, NULL) 1577 | PHP_FE_END /* Must be the last line in xmark_functions[] */ 1578 | }; 1579 | /* }}} */ 1580 | 1581 | /** {{{ module depends 1582 | */ 1583 | zend_module_dep xmark_deps[] = { 1584 | ZEND_MOD_CONFLICTS("xdebug") 1585 | ZEND_MOD_CONFLICTS("taint") 1586 | {NULL, NULL, NULL} 1587 | }; 1588 | /* }}} */ 1589 | 1590 | /* {{{ xmark_module_entry 1591 | */ 1592 | zend_module_entry xmark_module_entry = { 1593 | STANDARD_MODULE_HEADER_EX, NULL, 1594 | xmark_deps, 1595 | "xmark", 1596 | xmark_functions, 1597 | PHP_MINIT(xmark), 1598 | PHP_MSHUTDOWN(xmark), 1599 | PHP_RINIT(xmark), /* Replace with NULL if there's nothing to do at request start */ 1600 | PHP_RSHUTDOWN(xmark), /* Replace with NULL if there's nothing to do at request end */ 1601 | PHP_MINFO(xmark), 1602 | PHP_XMARK_VERSION, 1603 | PHP_MODULE_GLOBALS(xmark), 1604 | NULL, 1605 | NULL, 1606 | NULL, 1607 | STANDARD_MODULE_PROPERTIES_EX 1608 | }; 1609 | /* }}} */ 1610 | 1611 | #ifdef COMPILE_DL_XMARK 1612 | #ifdef ZTS 1613 | ZEND_TSRMLS_CACHE_DEFINE() 1614 | #endif 1615 | ZEND_GET_MODULE(xmark) 1616 | #endif 1617 | 1618 | /* 1619 | * Local variables: 1620 | * tab-width: 4 1621 | * c-basic-offset: 4 1622 | * End: 1623 | * vim600: noet sw=4 ts=4 fdm=marker 1624 | * vim<600: noet sw=4 ts=4 1625 | */ 1626 | --------------------------------------------------------------------------------