mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Various improvements to fuzzer SAPIs
This commit is contained in:
parent
41f45647f9
commit
c4e2ca607f
25 changed files with 322 additions and 102 deletions
12
configure.ac
12
configure.ac
|
@ -897,6 +897,18 @@ else
|
|||
ZEND_DEBUG=no
|
||||
fi
|
||||
|
||||
PHP_ARG_ENABLE([debug-assertions],
|
||||
[whether to enable debug assertions in release mode],
|
||||
[AS_HELP_STRING([--enable-debug-assertions],
|
||||
[Compile with debug assertions even in release mode])],
|
||||
[no],
|
||||
[no])
|
||||
|
||||
if test "$PHP_DEBUG_ASSERTIONS" = "yes"; then
|
||||
PHP_DEBUG=1
|
||||
ZEND_DEBUG=yes
|
||||
fi
|
||||
|
||||
PHP_ARG_ENABLE([rtld-now],
|
||||
[whether to dlopen extensions with RTLD_NOW instead of RTLD_LAZY],
|
||||
[AS_HELP_STRING([--enable-rtld-now],
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
Fuzzing SAPI for PHP
|
||||
|
||||
Enable fuzzing targets with --enable-fuzzer switch.
|
||||
|
||||
Your compiler should support -fsanitize=address and you need
|
||||
to have Fuzzer library around.
|
||||
|
||||
When running `make` it creates these binaries in `sapi/fuzzer/`:
|
||||
* php-fuzz-parser - fuzzing language parser
|
||||
* php-fuzz-unserialize - fuzzing unserialize() function
|
||||
* php-fuzz-json - fuzzing JSON parser
|
||||
* php-fuzz-exif - fuzzing exif_read_data() function (use --enable-exif)
|
||||
* php-fuzz-mbstring - fuzzing mb_ereg[i] (requires --enable-mbstring)
|
50
sapi/fuzzer/README.md
Normal file
50
sapi/fuzzer/README.md
Normal file
|
@ -0,0 +1,50 @@
|
|||
Fuzzing SAPI for PHP
|
||||
--------------------
|
||||
|
||||
The following `./configure` options can be used to enable the fuzzing SAPI, as well as all availablefuzzers. If you don't build the exif/json/mbstring extensions, fuzzers for these extensions will not be built.
|
||||
|
||||
```sh
|
||||
./configure \
|
||||
--enable-fuzzer \
|
||||
--with-pic \
|
||||
--enable-debug-assertions \
|
||||
--enable-exif \
|
||||
--enable-json \
|
||||
--enable-mbstring
|
||||
```
|
||||
|
||||
The `--with-pic` option is required to avoid a linking failure. The `--enable-debug-assertions` option can be used to enable debug assertions despite the use of a release build.
|
||||
|
||||
You will need a recent version of clang that supports the `-fsanitize=fuzzer-no-link` option.
|
||||
|
||||
When running `make` it creates these binaries in `sapi/fuzzer/`:
|
||||
|
||||
* `php-fuzz-parser`: Fuzzing language parser and compiler
|
||||
* `php-fuzz-unserialize`: Fuzzing unserialize() function
|
||||
* `php-fuzz-json`: Fuzzing JSON parser (requires --enable-json)
|
||||
* `php-fuzz-exif`: Fuzzing `exif_read_data()` function (requires --enable-exif)
|
||||
* `php-fuzz-mbstring`: fuzzing `mb_ereg[i]()` (requires --enable-mbstring)
|
||||
|
||||
Some fuzzers have a seed corpus in `sapi/fuzzer/corpus`. You can use it as follows:
|
||||
|
||||
```sh
|
||||
cp -r sapi/fuzzer/corpus/exif ./my-exif-corpus
|
||||
sapi/fuzzer/php-fuzz-exif ./my-exif-corpus
|
||||
```
|
||||
|
||||
For the unserialize fuzzer, a dictionary of internal classes should be generated first:
|
||||
|
||||
```sh
|
||||
sapi/cli/php sapi/fuzzer/corpus/generate_unserialize_dict.php
|
||||
cp -r sapi/fuzzer/corpus/unserialize ./my-unserialize-corpus
|
||||
sapi/fuzzer/php-fuzz-unserialize -dict=$PWD/sapi/fuzzer/corpus/unserialize.dict ./my-unserialize-corpus
|
||||
```
|
||||
|
||||
For the parser fuzzer, a corpus may be generated from Zend test files:
|
||||
|
||||
```sh
|
||||
sapi/cli/php sapi/fuzzer/corpus/generate_parser_corpus.php
|
||||
mkdir ./my-parser-corpus
|
||||
sapi/fuzzer/php-fuzz-parser -merge=1 ./my-parser-corpus sapi/fuzzer/corpus/parser
|
||||
sapi/fuzzer/php-fuzz-parser -only_ascii=1 ./my-parser-corpus
|
||||
```
|
|
@ -14,7 +14,8 @@ dnl
|
|||
AC_DEFUN([PHP_FUZZER_TARGET], [
|
||||
PHP_FUZZER_BINARIES="$PHP_FUZZER_BINARIES $SAPI_FUZZER_PATH/php-fuzz-$1"
|
||||
PHP_SUBST($2)
|
||||
PHP_ADD_SOURCES_X([sapi/fuzzer],[fuzzer-$1.c fuzzer-sapi.c],[],$2)
|
||||
PHP_ADD_SOURCES_X([sapi/fuzzer],[fuzzer-$1.c],[],$2)
|
||||
$2="[$]$2 $FUZZER_COMMON_OBJS"
|
||||
])
|
||||
|
||||
if test "$PHP_FUZZER" != "no"; then
|
||||
|
@ -24,14 +25,20 @@ if test "$PHP_FUZZER" != "no"; then
|
|||
SAPI_FUZZER_PATH=sapi/fuzzer
|
||||
PHP_SUBST(SAPI_FUZZER_PATH)
|
||||
if test -z "$LIB_FUZZING_ENGINE"; then
|
||||
FUZZING_LIB="-lFuzzer"
|
||||
FUZZING_LIB="-fsanitize=fuzzer"
|
||||
FUZZING_CC="$CC"
|
||||
AX_CHECK_COMPILE_FLAG([-fsanitize=address], [
|
||||
CFLAGS="$CFLAGS -fsanitize=address"
|
||||
CXXFLAGS="$CXXFLAGS -fsanitize=address"
|
||||
LDFLAGS="$LDFLAGS -fsanitize=address"
|
||||
dnl Don't include -fundefined in CXXFLAGS, because that would also require linking
|
||||
dnl with a C++ compiler.
|
||||
AX_CHECK_COMPILE_FLAG([-fsanitize=fuzzer-no-link], [
|
||||
CFLAGS="$CFLAGS -fsanitize=fuzzer-no-link,address"
|
||||
dnl Disable object-size sanitizer, because it is incompatible with our zend_function
|
||||
dnl union, and this can't be easily fixed.
|
||||
dnl We need to specify -fno-sanitize-recover=undefined here, otherwise ubsan warnings
|
||||
dnl will not be considered failures by the fuzzer.
|
||||
CFLAGS="$CFLAGS -fsanitize=undefined -fno-sanitize=object-size -fno-sanitize-recover=undefined"
|
||||
CXXFLAGS="$CXXFLAGS -fsanitize=fuzzer-no-link,address"
|
||||
],[
|
||||
AC_MSG_ERROR(compiler doesn't support -fsanitize flags)
|
||||
AC_MSG_ERROR(Compiler doesn't support -fsanitize=fuzzer-no-link)
|
||||
])
|
||||
else
|
||||
FUZZING_LIB="-lFuzzingEngine"
|
||||
|
@ -44,15 +51,21 @@ if test "$PHP_FUZZER" != "no"; then
|
|||
|
||||
PHP_ADD_BUILD_DIR([sapi/fuzzer])
|
||||
PHP_FUZZER_BINARIES=""
|
||||
PHP_BINARIES="$PHP_BINARIES fuzzer"
|
||||
PHP_INSTALLED_SAPIS="$PHP_INSTALLED_SAPIS fuzzer"
|
||||
|
||||
PHP_ADD_SOURCES_X([sapi/fuzzer], [fuzzer-sapi.c], [], FUZZER_COMMON_OBJS)
|
||||
|
||||
PHP_FUZZER_TARGET([parser], PHP_FUZZER_PARSER_OBJS)
|
||||
PHP_FUZZER_TARGET([unserialize], PHP_FUZZER_UNSERIALIZE_OBJS)
|
||||
PHP_FUZZER_TARGET([exif], PHP_FUZZER_EXIF_OBJS)
|
||||
|
||||
if test -n "$enable_json" && test "$enable_json" != "no"; then
|
||||
dnl json extension is enabled by default
|
||||
if (test -n "$enable_json" && test "$enable_json" != "no") || test -z "$PHP_ENABLE_ALL"; then
|
||||
PHP_FUZZER_TARGET([json], PHP_FUZZER_JSON_OBJS)
|
||||
fi
|
||||
if test -n "$enable_exif" && test "$enable_exif" != "no"; then
|
||||
PHP_FUZZER_TARGET([exif], PHP_FUZZER_EXIF_OBJS)
|
||||
fi
|
||||
if test -n "$enable_mbstring" && test "$enable_mbstring" != "no"; then
|
||||
PHP_FUZZER_TARGET([mbstring], PHP_FUZZER_MBSTRING_OBJS)
|
||||
fi
|
||||
|
|
22
sapi/fuzzer/corpus/generate_parser_corpus.php
Normal file
22
sapi/fuzzer/corpus/generate_parser_corpus.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
$testsDir = __DIR__ . '/../../../Zend/tests/';
|
||||
$it = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($testsDir),
|
||||
RecursiveIteratorIterator::LEAVES_ONLY
|
||||
);
|
||||
|
||||
$corpusDir = __DIR__ . '/parser';
|
||||
@mkdir($corpusDir);
|
||||
|
||||
foreach ($it as $file) {
|
||||
if (!preg_match('/\.phpt$/', $file)) continue;
|
||||
$code = file_get_contents($file);
|
||||
if (!preg_match('/--FILE--(.*)--EXPECT/s', $code, $matches)) continue;
|
||||
$code = $matches[1];
|
||||
|
||||
$outFile = str_replace($testsDir, '', $file);
|
||||
$outFile = str_replace('/', '_', $outFile);
|
||||
$outFile = $corpusDir . '/' . $outFile;
|
||||
file_put_contents($outFile, $code);
|
||||
}
|
9
sapi/fuzzer/corpus/generate_unserialize_dict.php
Normal file
9
sapi/fuzzer/corpus/generate_unserialize_dict.php
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
$dict = "";
|
||||
foreach (get_declared_classes() as $class) {
|
||||
$len = strlen($class);
|
||||
$dict .= "\"$len:\\\"$class\\\"\"\n";
|
||||
}
|
||||
|
||||
file_put_contents(__DIR__ . "/unserialize.dict", $dict);
|
85
sapi/fuzzer/corpus/parser.dict
Normal file
85
sapi/fuzzer/corpus/parser.dict
Normal file
|
@ -0,0 +1,85 @@
|
|||
"exit"
|
||||
"die"
|
||||
"fn"
|
||||
"function"
|
||||
"const"
|
||||
"return"
|
||||
"yield"
|
||||
"yield from"
|
||||
"try"
|
||||
"catch"
|
||||
"finally"
|
||||
"throw"
|
||||
"if"
|
||||
"elseif"
|
||||
"endif"
|
||||
"else"
|
||||
"while"
|
||||
"endwhile"
|
||||
"do"
|
||||
"for"
|
||||
"endfor"
|
||||
"foreach"
|
||||
"endforeach"
|
||||
"declare"
|
||||
"enddeclare"
|
||||
"instanceof"
|
||||
"as"
|
||||
"switch"
|
||||
"endswitch"
|
||||
"case"
|
||||
"default"
|
||||
"break"
|
||||
"continue"
|
||||
"goto"
|
||||
"echo"
|
||||
"print"
|
||||
"class"
|
||||
"interface"
|
||||
"trait"
|
||||
"extends"
|
||||
"implements"
|
||||
"new"
|
||||
"clone"
|
||||
"var"
|
||||
"int"
|
||||
"integer"
|
||||
"float"
|
||||
"double"
|
||||
"real"
|
||||
"string"
|
||||
"binary"
|
||||
"array"
|
||||
"object"
|
||||
"bool"
|
||||
"boolean"
|
||||
"unset"
|
||||
"eval"
|
||||
"include"
|
||||
"include_once"
|
||||
"require"
|
||||
"require_once"
|
||||
"namespace"
|
||||
"use"
|
||||
"insteadof"
|
||||
"global"
|
||||
"isset"
|
||||
"empty"
|
||||
"__halt_compiler"
|
||||
"static"
|
||||
"abstract"
|
||||
"final"
|
||||
"private"
|
||||
"protected"
|
||||
"public"
|
||||
"unset"
|
||||
"list"
|
||||
"callable"
|
||||
"__class__"
|
||||
"__trait__"
|
||||
"__function__"
|
||||
"__method__"
|
||||
"__line__"
|
||||
"__file__"
|
||||
"__dir__"
|
||||
"__namespace__"
|
1
sapi/fuzzer/corpus/unserialize/__serialize_007
Normal file
1
sapi/fuzzer/corpus/unserialize/__serialize_007
Normal file
|
@ -0,0 +1 @@
|
|||
O:13:"ArrayIterator":2:{i:0;i:0;s:1:"x";R:2;}
|
1
sapi/fuzzer/corpus/unserialize/bug7131
Normal file
1
sapi/fuzzer/corpus/unserialize/bug7131
Normal file
|
@ -0,0 +1 @@
|
|||
C:11:"ArrayObject":11:{x:i:0;r:3;X}
|
1
sapi/fuzzer/corpus/unserialize/bug71313
Normal file
1
sapi/fuzzer/corpus/unserialize/bug71313
Normal file
|
@ -0,0 +1 @@
|
|||
C:16:"SplObjectStorage":113:{x:i:2;O:8:"stdClass":0:{},a:2:{s:4:"prev";i:2;s:4:"next";O:8:"stdClass":0:{}};r:7;,R:2;s:4:"next";;r:3;};m:a:0:{}}
|
1
sapi/fuzzer/corpus/unserialize/bug73144_1
Normal file
1
sapi/fuzzer/corpus/unserialize/bug73144_1
Normal file
|
@ -0,0 +1 @@
|
|||
a:2:{i:0;O:1:"0":2:0s:1:"0";i:0;s:1:"0";a:1:{i:0;C:11:"ArrayObject":7:{x:i:0;r}
|
1
sapi/fuzzer/corpus/unserialize/bug73144_2
Normal file
1
sapi/fuzzer/corpus/unserialize/bug73144_2
Normal file
|
@ -0,0 +1 @@
|
|||
C:11:"ArrayObject":34:{x:i:1;O:8:"stdClass":1:{};m:a:0:{}}
|
1
sapi/fuzzer/corpus/unserialize/bug73825
Normal file
1
sapi/fuzzer/corpus/unserialize/bug73825
Normal file
|
@ -0,0 +1 @@
|
|||
O:8:"00000000":
|
1
sapi/fuzzer/corpus/unserialize/bug74101
Normal file
1
sapi/fuzzer/corpus/unserialize/bug74101
Normal file
|
@ -0,0 +1 @@
|
|||
O:9:"Exception":799999999999999999999999999997:0i:0;a:0:{}i:2;i:0;i:0;R:2;
|
1
sapi/fuzzer/corpus/unserialize/bug74103
Normal file
1
sapi/fuzzer/corpus/unserialize/bug74103
Normal file
|
@ -0,0 +1 @@
|
|||
a:7:{i:0;i:04;s:1:"a";i:2;i:9617006;i:4;s:1:"a";i:4;s:1:"a";R:5;s:1:"7";R:3;s:1:"a";R:5;;s:18;}}
|
1
sapi/fuzzer/corpus/unserialize/bug74111
Normal file
1
sapi/fuzzer/corpus/unserialize/bug74111
Normal file
|
@ -0,0 +1 @@
|
|||
O:8:"stdClass":00000000
|
1
sapi/fuzzer/corpus/unserialize/bug74614
Normal file
1
sapi/fuzzer/corpus/unserialize/bug74614
Normal file
|
@ -0,0 +1 @@
|
|||
a:3020000000000000000000000000000001:{i:0;a:0:{}i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;R:2;}
|
1
sapi/fuzzer/corpus/unserialize/bug75054
Normal file
1
sapi/fuzzer/corpus/unserialize/bug75054
Normal file
|
@ -0,0 +1 @@
|
|||
a:9:{i:0;s:4:"0000";i:0;s:4:"0000";i:0;R:2;s:4:"5003";R:2;s:4:"0000";R:2;s:4:"0000";R:2;s:4:"000";R:2;s:4:"0000";d:0;s:4:"0000";a:9:{s:4:"0000";
|
|
@ -33,11 +33,11 @@
|
|||
#include "fuzzer-sapi.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
#if HAVE_EXIF
|
||||
char *filename;
|
||||
int filedes;
|
||||
|
||||
if (php_request_startup()==FAILURE) {
|
||||
php_module_shutdown();
|
||||
if (fuzzer_request_startup() == FAILURE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,10 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
|||
php_request_shutdown(NULL);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
fprintf(stderr, "\n\nERROR:\nPHP built without EXIF, recompile with --enable-exif to use this fuzzer\n");
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
int LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||
|
|
|
@ -41,8 +41,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
|||
memcpy(data, Data, Size);
|
||||
data[Size] = '\0';
|
||||
|
||||
if (php_request_startup()==FAILURE) {
|
||||
php_module_shutdown();
|
||||
if (fuzzer_request_startup() == FAILURE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -50,9 +49,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
|||
zval result;
|
||||
php_json_parser parser;
|
||||
php_json_parser_init(&parser, &result, data, Size, option, 10);
|
||||
php_json_yyparse(&parser);
|
||||
|
||||
ZVAL_UNDEF(&result);
|
||||
if (php_json_yyparse(&parser) == SUCCESS) {
|
||||
zval_ptr_dtor(&result);
|
||||
}
|
||||
}
|
||||
|
||||
php_request_shutdown(NULL);
|
||||
|
|
|
@ -36,8 +36,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
|||
memcpy(data, Data, Size);
|
||||
data[Size] = '\0';
|
||||
|
||||
if (php_request_startup()==FAILURE) {
|
||||
php_module_shutdown();
|
||||
if (fuzzer_request_startup() == FAILURE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,56 +23,26 @@
|
|||
#include <ext/standard/info.h>
|
||||
#include <ext/standard/php_var.h>
|
||||
#include <main/php_variables.h>
|
||||
#ifdef JO0
|
||||
#include <ext/standard/php_smart_str.h>
|
||||
#endif
|
||||
|
||||
#include "fuzzer.h"
|
||||
|
||||
#include "fuzzer-sapi.h"
|
||||
|
||||
int fuzzer_do_parse(zend_file_handle *file_handle, char *filename)
|
||||
{
|
||||
int retval = FAILURE; /* failure by default */
|
||||
|
||||
SG(options) |= SAPI_OPTION_NO_CHDIR;
|
||||
SG(request_info).argc=0;
|
||||
SG(request_info).argv=NULL;
|
||||
|
||||
if (php_request_startup(TSRMLS_C)==FAILURE) {
|
||||
php_module_shutdown(TSRMLS_C);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
SG(headers_sent) = 1;
|
||||
SG(request_info).no_headers = 1;
|
||||
php_register_variable("PHP_SELF", filename, NULL TSRMLS_CC);
|
||||
|
||||
zend_first_try {
|
||||
zend_compile_file(file_handle, ZEND_REQUIRE);
|
||||
//retval = php_execute_script(file_handle TSRMLS_CC);
|
||||
} zend_end_try();
|
||||
|
||||
php_request_shutdown((void *) 0);
|
||||
|
||||
return (retval == SUCCESS) ? SUCCESS : FAILURE;
|
||||
}
|
||||
|
||||
int fuzzer_do_request_d(char *filename, char *data, size_t data_len);
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
char *s = malloc(Size+1);
|
||||
memcpy(s, Data, Size);
|
||||
s[Size] = '\0';
|
||||
|
||||
fuzzer_do_request_d("fuzzer.php", Data, Size);
|
||||
//fuzzer_do_parse(&file_handle, "fuzzer.php");
|
||||
fuzzer_do_request_from_buffer("fuzzer.php", s, Size);
|
||||
|
||||
free(s);
|
||||
/* Do not free s: fuzzer_do_request_from_buffer() takes ownership of the allocation. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||
/* Compilation will often trigger fatal errors.
|
||||
* Use tracked allocation mode to avoid leaks in that case. */
|
||||
putenv("USE_TRACKED_ALLOC=1");
|
||||
|
||||
fuzzer_init_php();
|
||||
|
||||
/* fuzzer_shutdown_php(); */
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
#include <ext/standard/php_var.h>
|
||||
#include <main/php_variables.h>
|
||||
|
||||
#ifdef __SANITIZE_ADDRESS__
|
||||
# include "sanitizer/lsan_interface.h"
|
||||
#endif
|
||||
|
||||
#include "fuzzer.h"
|
||||
#include "fuzzer-sapi.h"
|
||||
|
||||
|
@ -31,7 +35,8 @@ const char HARDCODED_INI[] =
|
|||
"html_errors=0\n"
|
||||
"implicit_flush=1\n"
|
||||
"max_execution_time=20\n"
|
||||
"output_buffering=0\n";
|
||||
"output_buffering=0\n"
|
||||
"error_reporting=0";
|
||||
|
||||
static int startup(sapi_module_struct *sapi_module)
|
||||
{
|
||||
|
@ -41,7 +46,7 @@ static int startup(sapi_module_struct *sapi_module)
|
|||
return SUCCESS;
|
||||
}
|
||||
|
||||
static size_t ub_write(const char *str, size_t str_length TSRMLS_DC)
|
||||
static size_t ub_write(const char *str, size_t str_length)
|
||||
{
|
||||
/* quiet */
|
||||
return str_length;
|
||||
|
@ -52,22 +57,22 @@ static void fuzzer_flush(void *server_context)
|
|||
/* quiet */
|
||||
}
|
||||
|
||||
static void send_header(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC)
|
||||
static void send_header(sapi_header_struct *sapi_header, void *server_context)
|
||||
{
|
||||
}
|
||||
|
||||
static char* read_cookies(TSRMLS_D)
|
||||
static char* read_cookies()
|
||||
{
|
||||
/* TODO: fuzz these! */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void register_variables(zval *track_vars_array TSRMLS_DC)
|
||||
static void register_variables(zval *track_vars_array)
|
||||
{
|
||||
php_import_environment_variables(track_vars_array TSRMLS_CC);
|
||||
php_import_environment_variables(track_vars_array);
|
||||
}
|
||||
|
||||
static void log_message(char *message, int level TSRMLS_DC)
|
||||
static void log_message(char *message, int level)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -106,6 +111,12 @@ static sapi_module_struct fuzzer_module = {
|
|||
|
||||
int fuzzer_init_php()
|
||||
{
|
||||
#ifdef __SANITIZE_ADDRESS__
|
||||
/* We're going to leak all the memory allocated during startup,
|
||||
* so disable lsan temporarily. */
|
||||
__lsan_disable();
|
||||
#endif
|
||||
|
||||
sapi_startup(&fuzzer_module);
|
||||
fuzzer_module.phpinfo_as_text = 1;
|
||||
|
||||
|
@ -118,15 +129,30 @@ int fuzzer_init_php()
|
|||
*/
|
||||
putenv("USE_ZEND_ALLOC=0");
|
||||
|
||||
#ifdef __SANITIZE_ADDRESS__
|
||||
/* Not very interested in memory leak detection, since Zend MM does that */
|
||||
__lsan_disable();
|
||||
#endif
|
||||
|
||||
if (fuzzer_module.startup(&fuzzer_module)==FAILURE) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
#ifdef __SANITIZE_ADDRESS__
|
||||
__lsan_enable();
|
||||
#endif
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int fuzzer_request_startup()
|
||||
{
|
||||
if (php_request_startup() == FAILURE) {
|
||||
php_module_shutdown();
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
#ifdef ZEND_SIGNALS
|
||||
/* Some signal handlers will be overriden,
|
||||
* don't complain about them during shutdown. */
|
||||
SIGG(check) = 0;
|
||||
#endif
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -141,9 +167,7 @@ void fuzzer_set_ini_file(const char *file)
|
|||
|
||||
int fuzzer_shutdown_php()
|
||||
{
|
||||
TSRMLS_FETCH();
|
||||
|
||||
php_module_shutdown(TSRMLS_C);
|
||||
php_module_shutdown();
|
||||
sapi_shutdown();
|
||||
|
||||
free(fuzzer_module.ini_entries);
|
||||
|
@ -158,18 +182,25 @@ int fuzzer_do_request(zend_file_handle *file_handle, char *filename)
|
|||
SG(request_info).argc=0;
|
||||
SG(request_info).argv=NULL;
|
||||
|
||||
if (php_request_startup(TSRMLS_C)==FAILURE) {
|
||||
php_module_shutdown(TSRMLS_C);
|
||||
if (fuzzer_request_startup() == FAILURE) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
SG(headers_sent) = 1;
|
||||
SG(request_info).no_headers = 1;
|
||||
php_register_variable("PHP_SELF", filename, NULL TSRMLS_CC);
|
||||
php_register_variable("PHP_SELF", filename, NULL);
|
||||
|
||||
zend_first_try {
|
||||
zend_compile_file(file_handle, ZEND_REQUIRE);
|
||||
/*retval = php_execute_script(file_handle TSRMLS_CC);*/
|
||||
zend_op_array *op_array = zend_compile_file(file_handle, ZEND_REQUIRE);
|
||||
if (op_array) {
|
||||
destroy_op_array(op_array);
|
||||
efree(op_array);
|
||||
}
|
||||
if (EG(exception)) {
|
||||
zend_object_release(EG(exception));
|
||||
EG(exception) = NULL;
|
||||
}
|
||||
/*retval = php_execute_script(file_handle);*/
|
||||
} zend_end_try();
|
||||
|
||||
php_request_shutdown((void *) 0);
|
||||
|
@ -189,10 +220,11 @@ int fuzzer_do_request_f(char *filename)
|
|||
return fuzzer_do_request(&file_handle, filename);
|
||||
}
|
||||
|
||||
int fuzzer_do_request_d(char *filename, char *data, size_t data_len)
|
||||
int fuzzer_do_request_from_buffer(char *filename, char *data, size_t data_len)
|
||||
{
|
||||
zend_file_handle file_handle;
|
||||
file_handle.filename = filename;
|
||||
file_handle.free_filename = 0;
|
||||
file_handle.opened_path = NULL;
|
||||
file_handle.handle.stream.handle = NULL;
|
||||
file_handle.handle.stream.reader = (zend_stream_reader_t)_php_stream_read;
|
||||
|
@ -209,11 +241,10 @@ int fuzzer_do_request_d(char *filename, char *data, size_t data_len)
|
|||
// Call named PHP function with N zval arguments
|
||||
void fuzzer_call_php_func_zval(const char *func_name, int nargs, zval *args) {
|
||||
zval retval, func;
|
||||
int result;
|
||||
|
||||
ZVAL_STRING(&func, func_name);
|
||||
ZVAL_UNDEF(&retval);
|
||||
result = call_user_function(CG(function_table), NULL, &func, &retval, nargs, args);
|
||||
call_user_function(CG(function_table), NULL, &func, &retval, nargs, args);
|
||||
|
||||
// TODO: check result?
|
||||
/* to ensure retval is not broken */
|
||||
|
|
|
@ -18,5 +18,7 @@
|
|||
*/
|
||||
|
||||
int fuzzer_init_php();
|
||||
int fuzzer_request_startup();
|
||||
void fuzzer_call_php_func(const char *func_name, int nargs, char **params);
|
||||
void fuzzer_call_php_func_zval(const char *func_name, int nargs, zval *args);
|
||||
int fuzzer_do_request_from_buffer(char *filename, char *data, size_t data_len);
|
||||
|
|
|
@ -32,28 +32,54 @@
|
|||
#include "ext/standard/php_var.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
unsigned char *data = malloc(Size+1);
|
||||
unsigned char *orig_data = malloc(Size+1);
|
||||
zend_execute_data execute_data;
|
||||
zend_function func;
|
||||
|
||||
memcpy(data, Data, Size);
|
||||
data[Size] = '\0';
|
||||
memcpy(orig_data, Data, Size);
|
||||
orig_data[Size] = '\0';
|
||||
|
||||
if (php_request_startup()==FAILURE) {
|
||||
php_module_shutdown();
|
||||
if (fuzzer_request_startup()==FAILURE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set up a dummy stack frame so that exceptions may be thrown. */
|
||||
{
|
||||
memset(&execute_data, 0, sizeof(zend_execute_data));
|
||||
memset(&func, 0, sizeof(zend_function));
|
||||
|
||||
func.type = ZEND_INTERNAL_FUNCTION;
|
||||
func.common.function_name = ZSTR_EMPTY_ALLOC();
|
||||
execute_data.func = &func;
|
||||
EG(current_execute_data) = &execute_data;
|
||||
}
|
||||
|
||||
{
|
||||
const unsigned char *data = orig_data;
|
||||
zval result;
|
||||
ZVAL_UNDEF(&result);
|
||||
|
||||
php_unserialize_data_t var_hash;
|
||||
PHP_VAR_UNSERIALIZE_INIT(var_hash);
|
||||
php_var_unserialize(&result, &data, data + Size, &var_hash);
|
||||
php_var_unserialize(&result, (const unsigned char **) &data, data + Size, &var_hash);
|
||||
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
|
||||
|
||||
zval_ptr_dtor(&result);
|
||||
|
||||
/* Destroy any thrown exception. */
|
||||
if (EG(exception)) {
|
||||
zend_object_release(EG(exception));
|
||||
EG(exception) = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unserialize may create circular structure. Make sure we free them.
|
||||
* Two calls are performed to handle objects with destructors. */
|
||||
zend_gc_collect_cycles();
|
||||
zend_gc_collect_cycles();
|
||||
php_request_shutdown(NULL);
|
||||
|
||||
free(data);
|
||||
free(orig_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue