summaryrefslogtreecommitdiff
path: root/sapi
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2019-09-13 15:15:46 +0200
committerNikita Popov <nikita.ppv@gmail.com>2019-09-16 16:04:10 +0200
commitc4e2ca607f49d37564aaf34f5a48c5e59aca12a6 (patch)
tree9efd9c787f0f37f77e9da6d8dac1dfdb275ec4af /sapi
parent41f45647f90a44514fd18b16fdcec5cceebf1635 (diff)
downloadphp-git-c4e2ca607f49d37564aaf34f5a48c5e59aca12a6.tar.gz
Various improvements to fuzzer SAPIs
Diffstat (limited to 'sapi')
-rw-r--r--sapi/fuzzer/README13
-rw-r--r--sapi/fuzzer/README.md50
-rw-r--r--sapi/fuzzer/config.m431
-rw-r--r--sapi/fuzzer/corpus/generate_parser_corpus.php22
-rw-r--r--sapi/fuzzer/corpus/generate_unserialize_dict.php9
-rw-r--r--sapi/fuzzer/corpus/parser.dict85
-rw-r--r--sapi/fuzzer/corpus/unserialize/__serialize_0071
-rw-r--r--sapi/fuzzer/corpus/unserialize/bug71311
-rw-r--r--sapi/fuzzer/corpus/unserialize/bug713131
-rw-r--r--sapi/fuzzer/corpus/unserialize/bug73144_11
-rw-r--r--sapi/fuzzer/corpus/unserialize/bug73144_21
-rw-r--r--sapi/fuzzer/corpus/unserialize/bug738251
-rw-r--r--sapi/fuzzer/corpus/unserialize/bug741011
-rw-r--r--sapi/fuzzer/corpus/unserialize/bug741031
-rw-r--r--sapi/fuzzer/corpus/unserialize/bug741111
-rw-r--r--sapi/fuzzer/corpus/unserialize/bug746141
-rw-r--r--sapi/fuzzer/corpus/unserialize/bug750541
-rw-r--r--sapi/fuzzer/fuzzer-exif.c8
-rw-r--r--sapi/fuzzer/fuzzer-json.c9
-rw-r--r--sapi/fuzzer/fuzzer-mbstring.c3
-rw-r--r--sapi/fuzzer/fuzzer-parser.c42
-rw-r--r--sapi/fuzzer/fuzzer-sapi.c73
-rw-r--r--sapi/fuzzer/fuzzer-sapi.h2
-rw-r--r--sapi/fuzzer/fuzzer-unserialize.c50
24 files changed, 308 insertions, 100 deletions
diff --git a/sapi/fuzzer/README b/sapi/fuzzer/README
deleted file mode 100644
index e0aafcaadf..0000000000
--- a/sapi/fuzzer/README
+++ /dev/null
@@ -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)
diff --git a/sapi/fuzzer/README.md b/sapi/fuzzer/README.md
new file mode 100644
index 0000000000..2cdbb1c5a3
--- /dev/null
+++ b/sapi/fuzzer/README.md
@@ -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
+```
diff --git a/sapi/fuzzer/config.m4 b/sapi/fuzzer/config.m4
index 12cf99bf86..bf08123bfb 100644
--- a/sapi/fuzzer/config.m4
+++ b/sapi/fuzzer/config.m4
@@ -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
diff --git a/sapi/fuzzer/corpus/generate_parser_corpus.php b/sapi/fuzzer/corpus/generate_parser_corpus.php
new file mode 100644
index 0000000000..7d9cdf98d1
--- /dev/null
+++ b/sapi/fuzzer/corpus/generate_parser_corpus.php
@@ -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);
+}
diff --git a/sapi/fuzzer/corpus/generate_unserialize_dict.php b/sapi/fuzzer/corpus/generate_unserialize_dict.php
new file mode 100644
index 0000000000..4c20ed7e93
--- /dev/null
+++ b/sapi/fuzzer/corpus/generate_unserialize_dict.php
@@ -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);
diff --git a/sapi/fuzzer/corpus/parser.dict b/sapi/fuzzer/corpus/parser.dict
new file mode 100644
index 0000000000..8b382afac9
--- /dev/null
+++ b/sapi/fuzzer/corpus/parser.dict
@@ -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__"
diff --git a/sapi/fuzzer/corpus/unserialize/__serialize_007 b/sapi/fuzzer/corpus/unserialize/__serialize_007
new file mode 100644
index 0000000000..6709aca303
--- /dev/null
+++ b/sapi/fuzzer/corpus/unserialize/__serialize_007
@@ -0,0 +1 @@
+O:13:"ArrayIterator":2:{i:0;i:0;s:1:"x";R:2;}
diff --git a/sapi/fuzzer/corpus/unserialize/bug7131 b/sapi/fuzzer/corpus/unserialize/bug7131
new file mode 100644
index 0000000000..1ba49d8da1
--- /dev/null
+++ b/sapi/fuzzer/corpus/unserialize/bug7131
@@ -0,0 +1 @@
+C:11:"ArrayObject":11:{x:i:0;r:3;X} \ No newline at end of file
diff --git a/sapi/fuzzer/corpus/unserialize/bug71313 b/sapi/fuzzer/corpus/unserialize/bug71313
new file mode 100644
index 0000000000..4163b0350a
--- /dev/null
+++ b/sapi/fuzzer/corpus/unserialize/bug71313
@@ -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:{}} \ No newline at end of file
diff --git a/sapi/fuzzer/corpus/unserialize/bug73144_1 b/sapi/fuzzer/corpus/unserialize/bug73144_1
new file mode 100644
index 0000000000..0d6d600520
--- /dev/null
+++ b/sapi/fuzzer/corpus/unserialize/bug73144_1
@@ -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}
diff --git a/sapi/fuzzer/corpus/unserialize/bug73144_2 b/sapi/fuzzer/corpus/unserialize/bug73144_2
new file mode 100644
index 0000000000..d1145066c5
--- /dev/null
+++ b/sapi/fuzzer/corpus/unserialize/bug73144_2
@@ -0,0 +1 @@
+C:11:"ArrayObject":34:{x:i:1;O:8:"stdClass":1:{};m:a:0:{}} \ No newline at end of file
diff --git a/sapi/fuzzer/corpus/unserialize/bug73825 b/sapi/fuzzer/corpus/unserialize/bug73825
new file mode 100644
index 0000000000..dc9b0e4334
--- /dev/null
+++ b/sapi/fuzzer/corpus/unserialize/bug73825
@@ -0,0 +1 @@
+O:8:"00000000":
diff --git a/sapi/fuzzer/corpus/unserialize/bug74101 b/sapi/fuzzer/corpus/unserialize/bug74101
new file mode 100644
index 0000000000..b8d2fd2308
--- /dev/null
+++ b/sapi/fuzzer/corpus/unserialize/bug74101
@@ -0,0 +1 @@
+O:9:"Exception":799999999999999999999999999997:0i:0;a:0:{}i:2;i:0;i:0;R:2;
diff --git a/sapi/fuzzer/corpus/unserialize/bug74103 b/sapi/fuzzer/corpus/unserialize/bug74103
new file mode 100644
index 0000000000..ab2f70239c
--- /dev/null
+++ b/sapi/fuzzer/corpus/unserialize/bug74103
@@ -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;}}
diff --git a/sapi/fuzzer/corpus/unserialize/bug74111 b/sapi/fuzzer/corpus/unserialize/bug74111
new file mode 100644
index 0000000000..c1196ee7de
--- /dev/null
+++ b/sapi/fuzzer/corpus/unserialize/bug74111
@@ -0,0 +1 @@
+O:8:"stdClass":00000000
diff --git a/sapi/fuzzer/corpus/unserialize/bug74614 b/sapi/fuzzer/corpus/unserialize/bug74614
new file mode 100644
index 0000000000..c7174893fe
--- /dev/null
+++ b/sapi/fuzzer/corpus/unserialize/bug74614
@@ -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;}
diff --git a/sapi/fuzzer/corpus/unserialize/bug75054 b/sapi/fuzzer/corpus/unserialize/bug75054
new file mode 100644
index 0000000000..866e5b8fca
--- /dev/null
+++ b/sapi/fuzzer/corpus/unserialize/bug75054
@@ -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"; \ No newline at end of file
diff --git a/sapi/fuzzer/fuzzer-exif.c b/sapi/fuzzer/fuzzer-exif.c
index 574f3393d2..5217ebf14d 100644
--- a/sapi/fuzzer/fuzzer-exif.c
+++ b/sapi/fuzzer/fuzzer-exif.c
@@ -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) {
diff --git a/sapi/fuzzer/fuzzer-json.c b/sapi/fuzzer/fuzzer-json.c
index 0c619a22b9..4ebc931727 100644
--- a/sapi/fuzzer/fuzzer-json.c
+++ b/sapi/fuzzer/fuzzer-json.c
@@ -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);
diff --git a/sapi/fuzzer/fuzzer-mbstring.c b/sapi/fuzzer/fuzzer-mbstring.c
index aaeef1ce8a..9837d34205 100644
--- a/sapi/fuzzer/fuzzer-mbstring.c
+++ b/sapi/fuzzer/fuzzer-mbstring.c
@@ -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;
}
diff --git a/sapi/fuzzer/fuzzer-parser.c b/sapi/fuzzer/fuzzer-parser.c
index eb1e03b6e8..ac2a1a8f2b 100644
--- a/sapi/fuzzer/fuzzer-parser.c
+++ b/sapi/fuzzer/fuzzer-parser.c
@@ -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(); */
diff --git a/sapi/fuzzer/fuzzer-sapi.c b/sapi/fuzzer/fuzzer-sapi.c
index dd26c3c103..da1df37943 100644
--- a/sapi/fuzzer/fuzzer-sapi.c
+++ b/sapi/fuzzer/fuzzer-sapi.c
@@ -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");
+ if (fuzzer_module.startup(&fuzzer_module)==FAILURE) {
+ return FAILURE;
+ }
+
#ifdef __SANITIZE_ADDRESS__
- /* Not very interested in memory leak detection, since Zend MM does that */
- __lsan_disable();
+ __lsan_enable();
#endif
- if (fuzzer_module.startup(&fuzzer_module)==FAILURE) {
+ 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 */
diff --git a/sapi/fuzzer/fuzzer-sapi.h b/sapi/fuzzer/fuzzer-sapi.h
index 92ce95b86a..cce8080b2c 100644
--- a/sapi/fuzzer/fuzzer-sapi.h
+++ b/sapi/fuzzer/fuzzer-sapi.h
@@ -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);
diff --git a/sapi/fuzzer/fuzzer-unserialize.c b/sapi/fuzzer/fuzzer-unserialize.c
index 9b843f25fe..9cf040944a 100644
--- a/sapi/fuzzer/fuzzer-unserialize.c
+++ b/sapi/fuzzer/fuzzer-unserialize.c
@@ -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;
}
- 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;
- PHP_VAR_UNSERIALIZE_INIT(var_hash);
- php_var_unserialize(&result, &data, data + Size, &var_hash);
- PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+ 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, (const unsigned char **) &data, data + Size, &var_hash);
+ PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
- zval_ptr_dtor(&result);
+ 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;
}