Skip to content

Commit

Permalink
Add unserializehash fuzzer.
Browse files Browse the repository at this point in the history
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 <nikic@php.net>
  • Loading branch information
kohler and nikic committed Jun 30, 2020
1 parent ada776c commit 75ada66
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 0 deletions.
3 changes: 3 additions & 0 deletions sapi/fuzzer/Makefile.frag
Original file line number Diff line number Diff line change
Expand Up @@ -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 $@

Expand Down
9 changes: 9 additions & 0 deletions sapi/fuzzer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions sapi/fuzzer/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
104 changes: 104 additions & 0 deletions sapi/fuzzer/fuzzer-unserializehash.c
Original file line number Diff line number Diff line change
@@ -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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#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;
}
10 changes: 10 additions & 0 deletions sapi/fuzzer/generate_unserializehash_corpus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

$corpusDir = __DIR__ . '/corpus/unserializehash';
@mkdir($corpusDir);

foreach (hash_algos() as $algo) {
$ctx = hash_init($algo);
$algx = preg_replace('/[^-_a-zA-Z0-9]/', '_', $algo);
file_put_contents($corpusDir . '/' . $algx, "x|" . serialize($ctx));
}

0 comments on commit 75ada66

Please sign in to comment.