mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +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
|
ZEND_DEBUG=no
|
||||||
fi
|
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],
|
PHP_ARG_ENABLE([rtld-now],
|
||||||
[whether to dlopen extensions with RTLD_NOW instead of RTLD_LAZY],
|
[whether to dlopen extensions with RTLD_NOW instead of RTLD_LAZY],
|
||||||
[AS_HELP_STRING([--enable-rtld-now],
|
[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], [
|
AC_DEFUN([PHP_FUZZER_TARGET], [
|
||||||
PHP_FUZZER_BINARIES="$PHP_FUZZER_BINARIES $SAPI_FUZZER_PATH/php-fuzz-$1"
|
PHP_FUZZER_BINARIES="$PHP_FUZZER_BINARIES $SAPI_FUZZER_PATH/php-fuzz-$1"
|
||||||
PHP_SUBST($2)
|
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
|
if test "$PHP_FUZZER" != "no"; then
|
||||||
|
@ -24,14 +25,20 @@ if test "$PHP_FUZZER" != "no"; then
|
||||||
SAPI_FUZZER_PATH=sapi/fuzzer
|
SAPI_FUZZER_PATH=sapi/fuzzer
|
||||||
PHP_SUBST(SAPI_FUZZER_PATH)
|
PHP_SUBST(SAPI_FUZZER_PATH)
|
||||||
if test -z "$LIB_FUZZING_ENGINE"; then
|
if test -z "$LIB_FUZZING_ENGINE"; then
|
||||||
FUZZING_LIB="-lFuzzer"
|
FUZZING_LIB="-fsanitize=fuzzer"
|
||||||
FUZZING_CC="$CC"
|
FUZZING_CC="$CC"
|
||||||
AX_CHECK_COMPILE_FLAG([-fsanitize=address], [
|
dnl Don't include -fundefined in CXXFLAGS, because that would also require linking
|
||||||
CFLAGS="$CFLAGS -fsanitize=address"
|
dnl with a C++ compiler.
|
||||||
CXXFLAGS="$CXXFLAGS -fsanitize=address"
|
AX_CHECK_COMPILE_FLAG([-fsanitize=fuzzer-no-link], [
|
||||||
LDFLAGS="$LDFLAGS -fsanitize=address"
|
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
|
else
|
||||||
FUZZING_LIB="-lFuzzingEngine"
|
FUZZING_LIB="-lFuzzingEngine"
|
||||||
|
@ -44,15 +51,21 @@ if test "$PHP_FUZZER" != "no"; then
|
||||||
|
|
||||||
PHP_ADD_BUILD_DIR([sapi/fuzzer])
|
PHP_ADD_BUILD_DIR([sapi/fuzzer])
|
||||||
PHP_FUZZER_BINARIES=""
|
PHP_FUZZER_BINARIES=""
|
||||||
|
PHP_BINARIES="$PHP_BINARIES fuzzer"
|
||||||
PHP_INSTALLED_SAPIS="$PHP_INSTALLED_SAPIS 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([parser], PHP_FUZZER_PARSER_OBJS)
|
||||||
PHP_FUZZER_TARGET([unserialize], PHP_FUZZER_UNSERIALIZE_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)
|
PHP_FUZZER_TARGET([json], PHP_FUZZER_JSON_OBJS)
|
||||||
fi
|
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
|
if test -n "$enable_mbstring" && test "$enable_mbstring" != "no"; then
|
||||||
PHP_FUZZER_TARGET([mbstring], PHP_FUZZER_MBSTRING_OBJS)
|
PHP_FUZZER_TARGET([mbstring], PHP_FUZZER_MBSTRING_OBJS)
|
||||||
fi
|
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"
|
#include "fuzzer-sapi.h"
|
||||||
|
|
||||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||||
|
#if HAVE_EXIF
|
||||||
char *filename;
|
char *filename;
|
||||||
int filedes;
|
int filedes;
|
||||||
|
|
||||||
if (php_request_startup()==FAILURE) {
|
if (fuzzer_request_startup() == FAILURE) {
|
||||||
php_module_shutdown();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +54,10 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||||
php_request_shutdown(NULL);
|
php_request_shutdown(NULL);
|
||||||
|
|
||||||
return 0;
|
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) {
|
int LLVMFuzzerInitialize(int *argc, char ***argv) {
|
||||||
|
|
|
@ -41,8 +41,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||||
memcpy(data, Data, Size);
|
memcpy(data, Data, Size);
|
||||||
data[Size] = '\0';
|
data[Size] = '\0';
|
||||||
|
|
||||||
if (php_request_startup()==FAILURE) {
|
if (fuzzer_request_startup() == FAILURE) {
|
||||||
php_module_shutdown();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,9 +49,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||||
zval result;
|
zval result;
|
||||||
php_json_parser parser;
|
php_json_parser parser;
|
||||||
php_json_parser_init(&parser, &result, data, Size, option, 10);
|
php_json_parser_init(&parser, &result, data, Size, option, 10);
|
||||||
php_json_yyparse(&parser);
|
if (php_json_yyparse(&parser) == SUCCESS) {
|
||||||
|
zval_ptr_dtor(&result);
|
||||||
ZVAL_UNDEF(&result);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
php_request_shutdown(NULL);
|
php_request_shutdown(NULL);
|
||||||
|
|
|
@ -36,8 +36,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||||
memcpy(data, Data, Size);
|
memcpy(data, Data, Size);
|
||||||
data[Size] = '\0';
|
data[Size] = '\0';
|
||||||
|
|
||||||
if (php_request_startup()==FAILURE) {
|
if (fuzzer_request_startup() == FAILURE) {
|
||||||
php_module_shutdown();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,56 +23,26 @@
|
||||||
#include <ext/standard/info.h>
|
#include <ext/standard/info.h>
|
||||||
#include <ext/standard/php_var.h>
|
#include <ext/standard/php_var.h>
|
||||||
#include <main/php_variables.h>
|
#include <main/php_variables.h>
|
||||||
#ifdef JO0
|
|
||||||
#include <ext/standard/php_smart_str.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "fuzzer.h"
|
#include "fuzzer.h"
|
||||||
|
|
||||||
#include "fuzzer-sapi.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) {
|
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||||
char *s = malloc(Size+1);
|
char *s = malloc(Size+1);
|
||||||
memcpy(s, Data, Size);
|
memcpy(s, Data, Size);
|
||||||
s[Size] = '\0';
|
s[Size] = '\0';
|
||||||
|
|
||||||
fuzzer_do_request_d("fuzzer.php", Data, Size);
|
fuzzer_do_request_from_buffer("fuzzer.php", s, Size);
|
||||||
//fuzzer_do_parse(&file_handle, "fuzzer.php");
|
|
||||||
|
|
||||||
free(s);
|
/* Do not free s: fuzzer_do_request_from_buffer() takes ownership of the allocation. */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int LLVMFuzzerInitialize(int *argc, char ***argv) {
|
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_init_php();
|
||||||
|
|
||||||
/* fuzzer_shutdown_php(); */
|
/* fuzzer_shutdown_php(); */
|
||||||
|
|
|
@ -24,6 +24,10 @@
|
||||||
#include <ext/standard/php_var.h>
|
#include <ext/standard/php_var.h>
|
||||||
#include <main/php_variables.h>
|
#include <main/php_variables.h>
|
||||||
|
|
||||||
|
#ifdef __SANITIZE_ADDRESS__
|
||||||
|
# include "sanitizer/lsan_interface.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "fuzzer.h"
|
#include "fuzzer.h"
|
||||||
#include "fuzzer-sapi.h"
|
#include "fuzzer-sapi.h"
|
||||||
|
|
||||||
|
@ -31,7 +35,8 @@ const char HARDCODED_INI[] =
|
||||||
"html_errors=0\n"
|
"html_errors=0\n"
|
||||||
"implicit_flush=1\n"
|
"implicit_flush=1\n"
|
||||||
"max_execution_time=20\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)
|
static int startup(sapi_module_struct *sapi_module)
|
||||||
{
|
{
|
||||||
|
@ -41,7 +46,7 @@ static int startup(sapi_module_struct *sapi_module)
|
||||||
return SUCCESS;
|
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 */
|
/* quiet */
|
||||||
return str_length;
|
return str_length;
|
||||||
|
@ -52,22 +57,22 @@ static void fuzzer_flush(void *server_context)
|
||||||
/* quiet */
|
/* 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! */
|
/* TODO: fuzz these! */
|
||||||
return NULL;
|
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()
|
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);
|
sapi_startup(&fuzzer_module);
|
||||||
fuzzer_module.phpinfo_as_text = 1;
|
fuzzer_module.phpinfo_as_text = 1;
|
||||||
|
|
||||||
|
@ -118,15 +129,30 @@ int fuzzer_init_php()
|
||||||
*/
|
*/
|
||||||
putenv("USE_ZEND_ALLOC=0");
|
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) {
|
if (fuzzer_module.startup(&fuzzer_module)==FAILURE) {
|
||||||
return 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;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,9 +167,7 @@ void fuzzer_set_ini_file(const char *file)
|
||||||
|
|
||||||
int fuzzer_shutdown_php()
|
int fuzzer_shutdown_php()
|
||||||
{
|
{
|
||||||
TSRMLS_FETCH();
|
php_module_shutdown();
|
||||||
|
|
||||||
php_module_shutdown(TSRMLS_C);
|
|
||||||
sapi_shutdown();
|
sapi_shutdown();
|
||||||
|
|
||||||
free(fuzzer_module.ini_entries);
|
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).argc=0;
|
||||||
SG(request_info).argv=NULL;
|
SG(request_info).argv=NULL;
|
||||||
|
|
||||||
if (php_request_startup(TSRMLS_C)==FAILURE) {
|
if (fuzzer_request_startup() == FAILURE) {
|
||||||
php_module_shutdown(TSRMLS_C);
|
|
||||||
return FAILURE;
|
return FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
SG(headers_sent) = 1;
|
SG(headers_sent) = 1;
|
||||||
SG(request_info).no_headers = 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_first_try {
|
||||||
zend_compile_file(file_handle, ZEND_REQUIRE);
|
zend_op_array *op_array = zend_compile_file(file_handle, ZEND_REQUIRE);
|
||||||
/*retval = php_execute_script(file_handle TSRMLS_CC);*/
|
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();
|
} zend_end_try();
|
||||||
|
|
||||||
php_request_shutdown((void *) 0);
|
php_request_shutdown((void *) 0);
|
||||||
|
@ -189,10 +220,11 @@ int fuzzer_do_request_f(char *filename)
|
||||||
return fuzzer_do_request(&file_handle, 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;
|
zend_file_handle file_handle;
|
||||||
file_handle.filename = filename;
|
file_handle.filename = filename;
|
||||||
|
file_handle.free_filename = 0;
|
||||||
file_handle.opened_path = NULL;
|
file_handle.opened_path = NULL;
|
||||||
file_handle.handle.stream.handle = NULL;
|
file_handle.handle.stream.handle = NULL;
|
||||||
file_handle.handle.stream.reader = (zend_stream_reader_t)_php_stream_read;
|
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
|
// Call named PHP function with N zval arguments
|
||||||
void fuzzer_call_php_func_zval(const char *func_name, int nargs, zval *args) {
|
void fuzzer_call_php_func_zval(const char *func_name, int nargs, zval *args) {
|
||||||
zval retval, func;
|
zval retval, func;
|
||||||
int result;
|
|
||||||
|
|
||||||
ZVAL_STRING(&func, func_name);
|
ZVAL_STRING(&func, func_name);
|
||||||
ZVAL_UNDEF(&retval);
|
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?
|
// TODO: check result?
|
||||||
/* to ensure retval is not broken */
|
/* to ensure retval is not broken */
|
||||||
|
|
|
@ -18,5 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int fuzzer_init_php();
|
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(const char *func_name, int nargs, char **params);
|
||||||
void fuzzer_call_php_func_zval(const char *func_name, int nargs, zval *args);
|
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"
|
#include "ext/standard/php_var.h"
|
||||||
|
|
||||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
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);
|
memcpy(orig_data, Data, Size);
|
||||||
data[Size] = '\0';
|
orig_data[Size] = '\0';
|
||||||
|
|
||||||
if (php_request_startup()==FAILURE) {
|
if (fuzzer_request_startup()==FAILURE) {
|
||||||
php_module_shutdown();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
zval result;
|
/* 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));
|
||||||
|
|
||||||
php_unserialize_data_t var_hash;
|
func.type = ZEND_INTERNAL_FUNCTION;
|
||||||
PHP_VAR_UNSERIALIZE_INIT(var_hash);
|
func.common.function_name = ZSTR_EMPTY_ALLOC();
|
||||||
php_var_unserialize(&result, &data, data + Size, &var_hash);
|
execute_data.func = &func;
|
||||||
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
|
EG(current_execute_data) = &execute_data;
|
||||||
|
}
|
||||||
|
|
||||||
zval_ptr_dtor(&result);
|
{
|
||||||
|
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, (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);
|
php_request_shutdown(NULL);
|
||||||
|
|
||||||
free(data);
|
free(orig_data);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue