Skip to content

Commit

Permalink
Add experimental "execute" fuzzer
Browse files Browse the repository at this point in the history
  • Loading branch information
nikic committed Dec 20, 2019
1 parent 1d6325f commit 271a176
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 31 deletions.
3 changes: 3 additions & 0 deletions sapi/fuzzer/Makefile.frag
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ FUZZER_BUILD = $(LIBTOOL) --mode=link $(FUZZING_CC) -export-dynamic $(CFLAGS_CLE
$(SAPI_FUZZER_PATH)/php-fuzz-parser: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_PARSER_OBJS)
$(FUZZER_BUILD) $(PHP_FUZZER_PARSER_OBJS) -o $@

$(SAPI_FUZZER_PATH)/php-fuzz-execute: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_EXECUTE_OBJS)
$(FUZZER_BUILD) $(PHP_FUZZER_EXECUTE_OBJS) -o $@

$(SAPI_FUZZER_PATH)/php-fuzz-unserialize: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_UNSERIALIZE_OBJS)
$(FUZZER_BUILD) $(PHP_FUZZER_UNSERIALIZE_OBJS) -o $@

Expand Down
1 change: 1 addition & 0 deletions sapi/fuzzer/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ if test "$PHP_FUZZER" != "no"; then
PHP_ADD_SOURCES_X([sapi/fuzzer], [fuzzer-sapi.c], [], FUZZER_COMMON_OBJS)

PHP_FUZZER_TARGET([parser], PHP_FUZZER_PARSER_OBJS)
PHP_FUZZER_TARGET([execute], PHP_FUZZER_EXECUTE_OBJS)
PHP_FUZZER_TARGET([unserialize], PHP_FUZZER_UNSERIALIZE_OBJS)

dnl json extension is enabled by default
Expand Down
77 changes: 77 additions & 0 deletions sapi/fuzzer/fuzzer-execute.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Johannes Schlüter <johanes@php.net> |
| Stanislav Malyshev <stas@php.net> |
+----------------------------------------------------------------------+
*/

#include <main/php.h>
#include <main/php_main.h>
#include <main/SAPI.h>
#include <ext/standard/info.h>
#include <ext/standard/php_var.h>
#include <main/php_variables.h>

#include "fuzzer.h"
#include "fuzzer-sapi.h"

#define MAX_STEPS 10000
static uint32_t steps_left;

void fuzzer_execute_ex(zend_execute_data *execute_data) {
while (1) {
int ret;
if (--steps_left == 0) {
zend_bailout();
}

if ((ret = ((user_opcode_handler_t) EX(opline)->handler)(execute_data)) != 0) {
if (ret > 0) {
execute_data = EG(current_execute_data);
} else {
return;
}
}
}
}

int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
char *s;
if (Size > 32 * 1024) {
/* Large inputs have a large impact on fuzzer performance,
* but are unlikely to be necessary to reach new codepaths. */
return 0;
}

s = malloc(Size+1);
memcpy(s, Data, Size);
s[Size] = '\0';

steps_left = MAX_STEPS;
fuzzer_do_request_from_buffer("/fuzzer.php", s, Size, /* execute */ 1);

/* 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();
zend_execute_ex = fuzzer_execute_ex;

/* fuzzer_shutdown_php(); */
return 0;
}
2 changes: 1 addition & 1 deletion sapi/fuzzer/fuzzer-parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
memcpy(s, Data, Size);
s[Size] = '\0';

fuzzer_do_request_from_buffer("fuzzer.php", s, Size);
fuzzer_do_request_from_buffer("fuzzer.php", s, Size, /* execute */ 0);

/* Do not free s: fuzzer_do_request_from_buffer() takes ownership of the allocation. */
return 0;
Expand Down
49 changes: 26 additions & 23 deletions sapi/fuzzer/fuzzer-sapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <ext/standard/info.h>
#include <ext/standard/php_var.h>
#include <main/php_variables.h>
#include <zend_exceptions.c>

#ifdef __SANITIZE_ADDRESS__
# include "sanitizer/lsan_interface.h"
Expand All @@ -36,7 +37,17 @@ const char HARDCODED_INI[] =
"error_reporting=0\n"
/* Reduce oniguruma limits to speed up fuzzing */
"mbstring.regex_stack_limit=10000\n"
"mbstring.regex_retry_limit=10000";
"mbstring.regex_retry_limit=10000\n"
/* For the "execute" fuzzer disable some functions that are likely to have
* undesirable consequences (shell execution, file system writes). */
"allow_url_include=0\n"
"allow_url_fopen=0\n"
"open_basedir=/tmp\n"
"disable_functions=dl"
",shell_exec,exec,system,proc_open,popen,passthru,pcntl_exec"
",chgrp,chmod,chown,copy,file_put_contents,lchgrp,lchown,link,mkdir"
",move_uploaded_file,rename,rmdir,symlink,tempname,touch,unlink,fopen"
;

static int startup(sapi_module_struct *sapi_module)
{
Expand Down Expand Up @@ -174,7 +185,7 @@ int fuzzer_shutdown_php()
return SUCCESS;
}

int fuzzer_do_request(zend_file_handle *file_handle, char *filename)
int fuzzer_do_request(zend_file_handle *file_handle, char *filename, zend_bool execute)
{
int retval = FAILURE; /* failure by default */

Expand All @@ -186,41 +197,33 @@ int fuzzer_do_request(zend_file_handle *file_handle, char *filename)
return FAILURE;
}

SG(headers_sent) = 1;
SG(request_info).no_headers = 1;
// Commented out to avoid leaking the header callback.
//SG(headers_sent) = 1;
//SG(request_info).no_headers = 1;
php_register_variable("PHP_SELF", filename, NULL);

zend_first_try {
zend_op_array *op_array = zend_compile_file(file_handle, ZEND_REQUIRE);
CG(compiled_filename) = NULL; /* Why??? */
if (op_array) {
if (execute) {
zend_execute(op_array, NULL);
}
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_clear_exception();
} zend_end_try();

/* Make sure GC is always enabled during shutdown,
* even if it was disabled during exection of the script. */
gc_enable(1);
php_request_shutdown((void *) 0);

return (retval == SUCCESS) ? SUCCESS : FAILURE;
}


int fuzzer_do_request_f(char *filename)
{
zend_file_handle file_handle;
file_handle.type = ZEND_HANDLE_FILENAME;
file_handle.filename = filename;
file_handle.handle.fp = NULL;
file_handle.opened_path = NULL;

return fuzzer_do_request(&file_handle, filename);
}

int fuzzer_do_request_from_buffer(char *filename, char *data, size_t data_len)
int fuzzer_do_request_from_buffer(char *filename, char *data, size_t data_len, zend_bool execute)
{
zend_file_handle file_handle;
file_handle.filename = filename;
Expand All @@ -235,7 +238,7 @@ int fuzzer_do_request_from_buffer(char *filename, char *data, size_t data_len)
file_handle.len = data_len;
file_handle.type = ZEND_HANDLE_STREAM;

return fuzzer_do_request(&file_handle, filename);
return fuzzer_do_request(&file_handle, filename, execute);
}

// Call named PHP function with N zval arguments
Expand Down
2 changes: 1 addition & 1 deletion sapi/fuzzer/fuzzer-sapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ 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);
int fuzzer_do_request_from_buffer(char *filename, char *data, size_t data_len, zend_bool execute);
17 changes: 11 additions & 6 deletions sapi/fuzzer/generate_parser_corpus.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,24 @@
RecursiveIteratorIterator::LEAVES_ONLY
);

$corpusDir = __DIR__ . '/corpus/parser';
@mkdir($corpusDir);
$parserCorpusDir = __DIR__ . '/corpus/parser';
$executeCorpusDir = __DIR__ . '/corpus/execute';
@mkdir($parserCorpusDir);
@mkdir($executeCorpusDir);

$maxLen = 32 * 1024;
foreach ($it as $file) {
if (!preg_match('/\.phpt$/', $file)) continue;
$code = file_get_contents($file);
if (!preg_match('/--FILE--\R(.*?)\R--([_A-Z]+)--/s', $code, $matches)) continue;
$fullCode = file_get_contents($file);
if (!preg_match('/--FILE--\R(.*?)\R--([_A-Z]+)--/s', $fullCode, $matches)) continue;
$code = $matches[1];
if (strlen($code) > $maxLen) continue;

$outFile = str_replace($testsDir, '', $file);
$outFile = str_replace('/', '_', $outFile);
$outFile = $corpusDir . '/' . $outFile;
file_put_contents($outFile, $code);
file_put_contents($parserCorpusDir . '/' . $outFile, $code);

if (!preg_match('/SKIP_SLOW_TESTS|SKIP_PERF_SENSITIVE|USE_ZEND_ALLOC/', $fullCode)) {
file_put_contents($executeCorpusDir . '/' . $outFile, $code);
}
}

0 comments on commit 271a176

Please sign in to comment.