Skip to content

Commit 06a25c7

Browse files
committed
Add fuzzer for tracing jit
1 parent e69fb48 commit 06a25c7

File tree

6 files changed

+117
-27
lines changed

6 files changed

+117
-27
lines changed

sapi/fuzzer/Makefile.frag

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ $(SAPI_FUZZER_PATH)/php-fuzz-parser: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_F
88
$(SAPI_FUZZER_PATH)/php-fuzz-execute: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_EXECUTE_OBJS)
99
$(FUZZER_BUILD) $(PHP_FUZZER_EXECUTE_OBJS) -o $@
1010

11-
$(SAPI_FUZZER_PATH)/php-fuzz-jit: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_JIT_OBJS)
12-
$(FUZZER_BUILD) $(PHP_FUZZER_JIT_OBJS) -o $@
11+
$(SAPI_FUZZER_PATH)/php-fuzz-function-jit: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_FUNCTION_JIT_OBJS)
12+
$(FUZZER_BUILD) $(PHP_FUZZER_FUNCTION_JIT_OBJS) -o $@
13+
14+
$(SAPI_FUZZER_PATH)/php-fuzz-tracing-jit: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_TRACING_JIT_OBJS)
15+
$(FUZZER_BUILD) $(PHP_FUZZER_TRACING_JIT_OBJS) -o $@
1316

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

sapi/fuzzer/README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ When running `make` it creates these binaries in `sapi/fuzzer/`:
3030
* `php-fuzz-exif`: Fuzzing `exif_read_data()` function (requires --enable-exif)
3131
* `php-fuzz-mbstring`: Fuzzing `mb_ereg[i]()` (requires --enable-mbstring)
3232
* `php-fuzz-execute`: Fuzzing the executor
33-
* `php-fuzz-jit`: Fuzzing the function JIT (requires --enable-opcache)
33+
* `php-fuzz-function-jit`: Fuzzing the function JIT (requires --enable-opcache)
34+
* `php-fuzz-tracing-jit`: Fuzzing the tracing JIT (requires --enable-opcache)
3435

3536
Some fuzzers have a seed corpus in `sapi/fuzzer/corpus`. You can use it as follows:
3637

@@ -64,6 +65,13 @@ sapi/fuzzer/php-fuzz-parser -merge=1 ./my-parser-corpus sapi/fuzzer/corpus/parse
6465
sapi/fuzzer/php-fuzz-parser -only_ascii=1 ./my-parser-corpus
6566
```
6667

68+
For the execute, function-jit and tracing-jit fuzzers, a corpus may be generated from any set of test files:
69+
70+
```sh
71+
sapi/cli/php sapi/fuzzer/generate_execute_corpus.php ./execute-corpus Zend/tests ext/opcache/tests/jit
72+
sapi/fuzzer/php-fuzzer-function-jit ./execute-corpus
73+
```
74+
6775
For the mbstring fuzzer, you may want to build the libonig dependency with instrumentation. At this time, libonig is not clean under ubsan, so only the fuzzer and address sanitizers may be used.
6876

6977
```sh

sapi/fuzzer/config.m4

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ if test "$PHP_FUZZER" != "no"; then
5454

5555
PHP_FUZZER_TARGET([parser], PHP_FUZZER_PARSER_OBJS)
5656
PHP_FUZZER_TARGET([execute], PHP_FUZZER_EXECUTE_OBJS)
57-
PHP_FUZZER_TARGET([jit], PHP_FUZZER_JIT_OBJS)
57+
PHP_FUZZER_TARGET([function-jit], PHP_FUZZER_FUNCTION_JIT_OBJS)
58+
PHP_FUZZER_TARGET([tracing-jit], PHP_FUZZER_TRACING_JIT_OBJS)
5859
PHP_FUZZER_TARGET([unserialize], PHP_FUZZER_UNSERIALIZE_OBJS)
5960
PHP_FUZZER_TARGET([unserializehash], PHP_FUZZER_UNSERIALIZEHASH_OBJS)
6061
PHP_FUZZER_TARGET([json], PHP_FUZZER_JSON_OBJS)

sapi/fuzzer/fuzzer-execute-common.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "fuzzer.h"
2020
#include "fuzzer-sapi.h"
21+
#include "zend_exceptions.h"
2122

2223
#define MAX_STEPS 1000
2324
#define MAX_SIZE (8 * 1024)
@@ -38,6 +39,8 @@ static zend_always_inline void fuzzer_step(void) {
3839
}
3940
}
4041

42+
static void (*orig_execute_ex)(zend_execute_data *execute_data);
43+
4144
static void fuzzer_execute_ex(zend_execute_data *execute_data) {
4245
while (1) {
4346
int ret;
@@ -91,9 +94,31 @@ static void fuzzer_init_php_for_execute(const char *extra_ini) {
9194

9295
fuzzer_init_php(extra_ini);
9396

97+
orig_execute_ex = zend_execute_ex;
9498
zend_execute_ex = fuzzer_execute_ex;
9599
orig_execute_internal = zend_execute_internal ? zend_execute_internal : execute_internal;
96100
zend_execute_internal = fuzzer_execute_internal;
97101
orig_compile_string = zend_compile_string;
98102
zend_compile_string = fuzzer_compile_string;
99103
}
104+
105+
ZEND_ATTRIBUTE_UNUSED static void opcache_invalidate(void) {
106+
steps_left = MAX_STEPS;
107+
zend_exception_save();
108+
zval retval, func, args[2];
109+
ZVAL_STRING(&func, "opcache_invalidate");
110+
ZVAL_STRING(&args[0], "/fuzzer.php");
111+
ZVAL_TRUE(&args[1]);
112+
call_user_function(CG(function_table), NULL, &func, &retval, 2, args);
113+
ZEND_ASSERT(Z_TYPE(retval) == IS_TRUE);
114+
zval_ptr_dtor(&args[0]);
115+
zval_ptr_dtor(&retval);
116+
zval_ptr_dtor(&func);
117+
zend_exception_restore();
118+
}
119+
120+
ZEND_ATTRIBUTE_UNUSED char *get_opcache_path(void) {
121+
// TODO: Make this more general.
122+
char *opcache_path = "modules/opcache.so";
123+
return realpath(opcache_path, NULL);
124+
}

sapi/fuzzer/fuzzer-jit.c renamed to sapi/fuzzer/fuzzer-function-jit.c

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,6 @@
1515
*/
1616

1717
#include "fuzzer-execute-common.h"
18-
#include "zend_exceptions.h"
19-
20-
static void opcache_invalidate(void) {
21-
steps_left = MAX_STEPS;
22-
zend_exception_save();
23-
zval retval, func, args[2];
24-
ZVAL_STRING(&func, "opcache_invalidate");
25-
ZVAL_STRING(&args[0], "/fuzzer.php");
26-
ZVAL_TRUE(&args[1]);
27-
call_user_function(CG(function_table), NULL, &func, &retval, 2, args);
28-
ZEND_ASSERT(Z_TYPE(retval) == IS_TRUE);
29-
zval_ptr_dtor(&args[0]);
30-
zval_ptr_dtor(&retval);
31-
zval_ptr_dtor(&func);
32-
zend_exception_restore();
33-
}
3418

3519
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
3620
if (Size > MAX_SIZE) {
@@ -63,12 +47,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
6347
return 0;
6448
}
6549

66-
char *get_opcache_path(void) {
67-
// TODO: Make this more general.
68-
char *opcache_path = "modules/opcache.so";
69-
return realpath(opcache_path, NULL);
70-
}
71-
7250
int LLVMFuzzerInitialize(int *argc, char ***argv) {
7351
char *opcache_path = get_opcache_path();
7452
assert(opcache_path && "Failed to determine opcache path");
@@ -78,7 +56,7 @@ int LLVMFuzzerInitialize(int *argc, char ***argv) {
7856
"zend_extension=%s\n"
7957
"opcache.validate_timestamps=0\n"
8058
"opcache.file_update_protection=0\n"
81-
"opcache.jit_buffer_size=512M",
59+
"opcache.jit_buffer_size=256M",
8260
opcache_path);
8361
free(opcache_path);
8462
fuzzer_init_php_for_execute(ini_buf);

sapi/fuzzer/fuzzer-tracing-jit.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| Copyright (c) The PHP Group |
4+
+----------------------------------------------------------------------+
5+
| This source file is subject to version 3.01 of the PHP license, |
6+
| that is bundled with this package in the file LICENSE, and is |
7+
| available through the world-wide-web at the following url: |
8+
| https://www.php.net/license/3_01.txt |
9+
| If you did not receive a copy of the PHP license and are unable to |
10+
| obtain it through the world-wide-web, please send a note to |
11+
| license@php.net so we can mail you a copy immediately. |
12+
+----------------------------------------------------------------------+
13+
| Authors: Nikita Popov <nikic@php.net> |
14+
+----------------------------------------------------------------------+
15+
*/
16+
17+
#include "fuzzer-execute-common.h"
18+
19+
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
20+
if (Size > MAX_SIZE) {
21+
/* Large inputs have a large impact on fuzzer performance,
22+
* but are unlikely to be necessary to reach new codepaths. */
23+
return 0;
24+
}
25+
26+
zend_string *jit_option = zend_string_init("opcache.jit", sizeof("opcache.jit") - 1, 1);
27+
28+
/* First run without JIT to determine whether we bail out. We should not run JITed code if
29+
* we bail out here, as the JIT code may loop infinitely. */
30+
steps_left = MAX_STEPS;
31+
bailed_out = false;
32+
zend_alter_ini_entry_chars(
33+
jit_option, "off", sizeof("off")-1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
34+
fuzzer_do_request_from_buffer(
35+
"/fuzzer.php", (const char *) Data, Size, /* execute */ 1, opcache_invalidate);
36+
37+
if (!bailed_out) {
38+
steps_left = MAX_STEPS;
39+
zend_alter_ini_entry_chars(jit_option,
40+
"tracing", sizeof("tracing")-1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
41+
zend_execute_ex = orig_execute_ex;
42+
/* Trace & compile */
43+
fuzzer_do_request_from_buffer(
44+
"/fuzzer.php", (const char *) Data, Size, /* execute */ 1, NULL);
45+
/* Execute trace */
46+
fuzzer_do_request_from_buffer(
47+
"/fuzzer.php", (const char *) Data, Size, /* execute */ 1, opcache_invalidate);
48+
zend_execute_ex = fuzzer_execute_ex;
49+
}
50+
51+
zend_string_release(jit_option);
52+
53+
return 0;
54+
}
55+
56+
int LLVMFuzzerInitialize(int *argc, char ***argv) {
57+
char *opcache_path = get_opcache_path();
58+
assert(opcache_path && "Failed to determine opcache path");
59+
60+
char ini_buf[512];
61+
snprintf(ini_buf, sizeof(ini_buf),
62+
"zend_extension=%s\n"
63+
"opcache.validate_timestamps=0\n"
64+
"opcache.file_update_protection=0\n"
65+
"opcache.jit_buffer_size=256M\n"
66+
"opcache.jit_hot_func=1\n"
67+
"opcache.jit_hot_loop=1\n"
68+
"opcache.jit_hot_return=1\n"
69+
"opcache.jit_hot_side_exit=1\n"
70+
"opcache.jit_max_root_traces=32768",
71+
opcache_path);
72+
free(opcache_path);
73+
fuzzer_init_php_for_execute(ini_buf);
74+
return 0;
75+
}

0 commit comments

Comments
 (0)