Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions sapi/fuzzer/Makefile.frag
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ $(SAPI_FUZZER_PATH)/php-fuzz-unserializehash: $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OB
$(SAPI_FUZZER_PATH)/php-fuzz-json: $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_FUZZER_JSON_OBJS)
$(FUZZER_BUILD) $(PHP_FUZZER_JSON_OBJS) -o $@

$(SAPI_FUZZER_PATH)/php-fuzz-uri: $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_FUZZER_URI_OBJS)
$(FUZZER_BUILD) $(PHP_FUZZER_URI_OBJS) -o $@

$(SAPI_FUZZER_PATH)/php-fuzz-exif: $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_FUZZER_EXIF_OBJS)
$(FUZZER_BUILD) $(PHP_FUZZER_EXIF_OBJS) -o $@

Expand Down
8 changes: 8 additions & 0 deletions sapi/fuzzer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ When running `make` it creates these binaries in `sapi/fuzzer/`:
* `php-fuzz-unserialize`: Fuzzing unserialize() function
* `php-fuzz-unserializehash`: Fuzzing unserialize() for HashContext objects
* `php-fuzz-json`: Fuzzing JSON parser
* `php-fuzz-uri`: Fuzzing the `Uri\Rfc3986\Uri` and `Uri\WhatWg\Url` classes and `parse_url()`
* `php-fuzz-exif`: Fuzzing `exif_read_data()` function (requires --enable-exif)
* `php-fuzz-mbstring`: Fuzzing `mb_convert_encoding()` (requires `--enable-mbstring`)
* `php-fuzz-mbregex`: Fuzzing `mb_ereg[i]()` (requires --enable-mbstring)
Expand Down Expand Up @@ -95,3 +96,10 @@ export ONIG_LIBS="-L$PWD/oniguruma/src/.libs -l:libonig.a"
```

This will link an instrumented libonig statically into the PHP binary.

For the uri fuzzer, a seed corpus is available:

```sh
cp -r sapi/fuzzer/corpus/uri ./my-uri-corpus
sapi/fuzzer/php-fuzz-uri ./my-uri-corpus
```
1 change: 1 addition & 0 deletions sapi/fuzzer/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ if test "$PHP_FUZZER" != "no"; then
PHP_FUZZER_TARGET([unserialize], [PHP_FUZZER_UNSERIALIZE_OBJS])
PHP_FUZZER_TARGET([unserializehash], [PHP_FUZZER_UNSERIALIZEHASH_OBJS])
PHP_FUZZER_TARGET([json], [PHP_FUZZER_JSON_OBJS])
PHP_FUZZER_TARGET([uri], [PHP_FUZZER_URI_OBJS])

if test -n "$enable_exif" && test "$enable_exif" != "no"; then
PHP_FUZZER_TARGET([exif], [PHP_FUZZER_EXIF_OBJS])
Expand Down
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed01
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://user:pass@example.com:8080/a/b/c?x=1&y=2#frag
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed02
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
http://[2001:db8::1]:80/path
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed03
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
http://[v1.fe80::a+en1]/
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed04
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
http://192.168.0.1./x
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed05
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
http://0x7f.1/
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed06
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
file:///C:/Windows/system32
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed07
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
file:///tmp/test.txt
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed08
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mailto:someone@example.org?subject=hi
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed09
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
urn:isbn:0451450523
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed10
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//example.com/network/path
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed11
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
../relative/./path/../x
2 changes: 2 additions & 0 deletions sapi/fuzzer/corpus/uri/seed12
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
https://example.com/
../other?q#f
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed13
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
http://xn--nxasmq6b.example/
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed14
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
http://日本.example/
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed15
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://example.com/%7Euser/%E2%82%AC
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed16
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
data:text/plain;base64,SGVsbG8=
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed17
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
HTTP://EXAMPLE.COM:/A%2fB
2 changes: 2 additions & 0 deletions sapi/fuzzer/corpus/uri/seed18
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
http://example.com
http://other.example:1/
2 changes: 2 additions & 0 deletions sapi/fuzzer/corpus/uri/seed19
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ws://example.com/chat
?query
1 change: 1 addition & 0 deletions sapi/fuzzer/corpus/uri/seed20
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foo://bar@[::ffff:1.2.3.4]:65535/
2 changes: 2 additions & 0 deletions sapi/fuzzer/corpus/uri/seed21
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
http://a/b/c/d;p?q
g;x=1/../y
150 changes: 150 additions & 0 deletions sapi/fuzzer/fuzzer-uri.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
+----------------------------------------------------------------------+
| Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
| This source file is subject to the Modified BSD License that is |
| bundled with this package in the file LICENSE, and is available |
| through the World Wide Web at <https://www.php.net/license/>. |
| |
| SPDX-License-Identifier: BSD-3-Clause |
+----------------------------------------------------------------------+
| Author: Yuma Kakei <yumasansansan@gmail.com> |
+----------------------------------------------------------------------+
*/

#include <main/php.h>
#include <zend_exceptions.h>

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

#define MAX_SIZE (8 * 1024)

static const char *rfc3986_getters[] = {
"getUriType", "getScheme", "getRawScheme", "getUserInfo", "getRawUserInfo",
"getUsername", "getRawUsername", "getPassword", "getRawPassword", "getHost",
"getRawHost", "getHostType", "getPort", "getPath", "getRawPath", "getQuery",
"getRawQuery", "getFragment", "getRawFragment", "toString", "toRawString",
"__serialize", "__debugInfo", NULL
};

static const char *rfc3986_withers[] = {
"withScheme", "withUserInfo", "withHost", "withPath", "withQuery", "withFragment", NULL
};

static const char *whatwg_getters[] = {
"getScheme", "isSpecialScheme", "getUsername", "getPassword", "getAsciiHost",
"getUnicodeHost", "getHostType", "getPort", "getPath", "getQuery", "getFragment",
"toAsciiString", "toUnicodeString", "__serialize", "__debugInfo", NULL
};

static const char *whatwg_withers[] = {
"withScheme", "withUsername", "withPassword", "withHost", "withPath", "withQuery",
"withFragment", NULL
};

/* Call a method, discarding its result and any exception it throws. If the result is an
* object, call to_string_method on it. */
static void call_method(
zend_object *obj, const char *name, uint32_t argc, zval *argv, const char *to_string_method)
{
zend_function *fn = zend_hash_str_find_ptr_lc(&obj->ce->function_table, name, strlen(name));
ZEND_ASSERT(fn != NULL);

zval retval;
ZVAL_UNDEF(&retval);
zend_call_known_instance_method(fn, obj, &retval, argc, argv);
zend_clear_exception();

if (to_string_method && Z_TYPE(retval) == IS_OBJECT) {
call_method(Z_OBJ(retval), to_string_method, 0, NULL, NULL);
}
zval_ptr_dtor(&retval);
}

static void fuzz_uri_class(
const char *class_name, size_t class_name_len, zval *uri, zval *ref,
const char **getters, const char **withers, const char *to_string_method)
{
zend_class_entry *ce = zend_hash_str_find_ptr(CG(class_table), class_name, class_name_len);
ZEND_ASSERT(ce != NULL);
zend_function *parse = zend_hash_str_find_ptr(&ce->function_table, ZEND_STRL("parse"));
ZEND_ASSERT(parse != NULL);

/* Url::parse() reports errors through its third, by-reference argument. */
zval args[3], retval;
ZVAL_COPY_VALUE(&args[0], uri);
ZVAL_NULL(&args[1]);
ZVAL_NEW_EMPTY_REF(&args[2]);
ZVAL_NULL(Z_REFVAL(args[2]));
ZVAL_UNDEF(&retval);
zend_call_known_function(parse, NULL, ce, &retval, parse->common.num_args, args, NULL);
zend_clear_exception();
zval_ptr_dtor(&args[2]);

if (Z_TYPE(retval) != IS_OBJECT) {
zval_ptr_dtor(&retval);
return;
}

zend_object *obj = Z_OBJ(retval);
for (const char **getter = getters; *getter; getter++) {
call_method(obj, *getter, 0, NULL, NULL);
}
call_method(obj, "equals", 1, &retval, NULL);

if (!Z_ISUNDEF_P(ref)) {
call_method(obj, "resolve", 1, ref, to_string_method);
for (const char **wither = withers; *wither; wither++) {
call_method(obj, *wither, 1, ref, to_string_method);
}
}

zval_ptr_dtor(&retval);
}

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

if (fuzzer_request_startup() == FAILURE) {
return 0;
}

fuzzer_setup_dummy_frame();

/* The input is a URI, optionally followed by a newline and a string that is passed to
* resolve() and the with*() methods. The WHATWG parser strips newlines from its input, so
* using them as separator loses little. */
const uint8_t *newline = memchr(Data, '\n', Size);
size_t uri_len = newline ? (size_t) (newline - Data) : Size;
zval uri, ref;
ZVAL_STRINGL(&uri, (const char *) Data, uri_len);
if (newline) {
ZVAL_STRINGL(&ref, (const char *) newline + 1, Size - uri_len - 1);
} else {
ZVAL_UNDEF(&ref);
}

fuzz_uri_class(ZEND_STRL("uri\\rfc3986\\uri"), &uri, &ref,
rfc3986_getters, rfc3986_withers, "toRawString");
fuzz_uri_class(ZEND_STRL("uri\\whatwg\\url"), &uri, &ref,
whatwg_getters, whatwg_withers, "toAsciiString");
fuzzer_call_php_func_zval("parse_url", 1, &uri);

zval_ptr_dtor(&uri);
zval_ptr_dtor(&ref);

fuzzer_request_shutdown();
return 0;
}

int LLVMFuzzerInitialize(int *argc, char ***argv) {
fuzzer_init_php(NULL);

/* fuzzer_shutdown_php(); */
return 0;
}
Loading