From: Eddie Kohler Date: Sat, 27 Jun 2020 04:16:43 +0000 (-0700) Subject: Add unserializehash fuzzer. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=75ada661b9264eb55587ccbfa3bcd7447c5163f3;p=php Add unserializehash fuzzer. Unlike the straight unserialize fuzzer, this runs only on HashContexts, and it does an update and finalize on the contexts it creates. Co-authored-by: Nikita Popov --- diff --git a/sapi/fuzzer/Makefile.frag b/sapi/fuzzer/Makefile.frag index 57952a8ff4..75a387d3d4 100644 --- a/sapi/fuzzer/Makefile.frag +++ b/sapi/fuzzer/Makefile.frag @@ -8,6 +8,9 @@ $(SAPI_FUZZER_PATH)/php-fuzz-parser: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_F $(SAPI_FUZZER_PATH)/php-fuzz-unserialize: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_UNSERIALIZE_OBJS) $(FUZZER_BUILD) $(PHP_FUZZER_UNSERIALIZE_OBJS) -o $@ +$(SAPI_FUZZER_PATH)/php-fuzz-unserializehash: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_UNSERIALIZEHASH_OBJS) + $(FUZZER_BUILD) $(PHP_FUZZER_UNSERIALIZEHASH_OBJS) -o $@ + $(SAPI_FUZZER_PATH)/php-fuzz-json: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_JSON_OBJS) $(FUZZER_BUILD) $(PHP_FUZZER_JSON_OBJS) -o $@ diff --git a/sapi/fuzzer/README.md b/sapi/fuzzer/README.md index 0645a04bc7..710a10f053 100644 --- a/sapi/fuzzer/README.md +++ b/sapi/fuzzer/README.md @@ -22,6 +22,7 @@ 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-unserializehash`: Fuzzing unserialize() for HashContext objects * `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) @@ -41,6 +42,14 @@ cp -r sapi/fuzzer/corpus/unserialize ./my-unserialize-corpus sapi/fuzzer/php-fuzz-unserialize -dict=$PWD/sapi/fuzzer/dict/unserialize ./my-unserialize-corpus ``` +For the unserializehash fuzzer, generate a corpus of initial hash serializations: + +```sh +sapi/cli/php sapi/fuzzer/generate_unserializehash_corpus.php +cp -r sapi/fuzzer/corpus/unserializehash ./my-unserialize-corpus +sapi/fuzzer/php-fuzz-unserializehash ./my-unserialize-corpus +``` + For the parser fuzzer, a corpus may be generated from Zend test files: ```sh diff --git a/sapi/fuzzer/config.m4 b/sapi/fuzzer/config.m4 index 8e15a274ec..a148985df9 100644 --- a/sapi/fuzzer/config.m4 +++ b/sapi/fuzzer/config.m4 @@ -76,6 +76,7 @@ if test "$PHP_FUZZER" != "no"; then PHP_FUZZER_TARGET([parser], PHP_FUZZER_PARSER_OBJS) PHP_FUZZER_TARGET([unserialize], PHP_FUZZER_UNSERIALIZE_OBJS) + PHP_FUZZER_TARGET([unserializehash], PHP_FUZZER_UNSERIALIZEHASH_OBJS) PHP_FUZZER_TARGET([json], PHP_FUZZER_JSON_OBJS) if test -n "$enable_exif" && test "$enable_exif" != "no"; then diff --git a/sapi/fuzzer/fuzzer-unserializehash.c b/sapi/fuzzer/fuzzer-unserializehash.c new file mode 100644 index 0000000000..9fbc10e6a8 --- /dev/null +++ b/sapi/fuzzer/fuzzer-unserializehash.c @@ -0,0 +1,104 @@ +/* + +----------------------------------------------------------------------+ + | 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. | + +----------------------------------------------------------------------+ + */ + + +#include "fuzzer.h" + +#include "Zend/zend.h" +#include "main/php_config.h" +#include "main/php_main.h" + +#include +#include +#include + +#include "fuzzer-sapi.h" + +#include "ext/standard/php_var.h" + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t FullSize) { + zend_execute_data execute_data; + zend_function func; + const uint8_t *Start = memchr(Data, '|', FullSize); + if (!Start) { + return 0; + } + ++Start; + + size_t Size = (Data + FullSize) - Start; + unsigned char *orig_data = malloc(Size+1); + memcpy(orig_data, Start, Size); + orig_data[Size] = '\0'; + + 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, (const unsigned char **) &data, data + Size, &var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + + if (Z_TYPE(result) == IS_OBJECT + && zend_string_equals_literal(Z_OBJCE(result)->name, "HashContext")) { + zval args[2]; + ZVAL_COPY_VALUE(&args[0], &result); + ZVAL_STRINGL(&args[1], (char *) Data, (Start - Data) - 1); + fuzzer_call_php_func_zval("hash_update", 2, args); + zval_ptr_dtor(&args[1]); + fuzzer_call_php_func_zval("hash_final", 1, args); + } + + 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(orig_data); + + return 0; +} + +int LLVMFuzzerInitialize(int *argc, char ***argv) { + fuzzer_init_php(); + + /* fuzzer_shutdown_php(); */ + return 0; +} diff --git a/sapi/fuzzer/generate_unserializehash_corpus.php b/sapi/fuzzer/generate_unserializehash_corpus.php new file mode 100644 index 0000000000..04c6ea1428 --- /dev/null +++ b/sapi/fuzzer/generate_unserializehash_corpus.php @@ -0,0 +1,10 @@ +