php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
fuzzer-execute-common.h
Go to the documentation of this file.
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 <main/php.h>
18
19#if defined(__FreeBSD__)
20# include <sys/sysctl.h>
21#endif
22
23#include "fuzzer.h"
24#include "fuzzer-sapi.h"
25#include "zend_exceptions.h"
26
27#define FILE_NAME "/tmp/fuzzer.php"
28#define MAX_STEPS 1000
29#define MAX_SIZE (8 * 1024)
30static uint32_t steps_left;
31static bool bailed_out = false;
32
33/* Because the fuzzer is always compiled with clang,
34 * we can assume that we don't use global registers / hybrid VM. */
36
37static zend_always_inline void fuzzer_bailout(void) {
38 bailed_out = true;
40}
41
42static zend_always_inline void fuzzer_step(void) {
43 if (--steps_left == 0) {
44 /* Reset steps before bailing out, so code running after bailout (e.g. in
45 * destructors) will get another MAX_STEPS, rather than UINT32_MAX steps. */
46 steps_left = MAX_STEPS;
47 fuzzer_bailout();
48 }
49}
50
51static void (*orig_execute_ex)(zend_execute_data *execute_data);
52
53static void fuzzer_execute_ex(zend_execute_data *execute_data) {
54 while (1) {
55 int ret;
56 fuzzer_step();
57 if ((ret = ((opcode_handler_t) EX(opline)->handler)(execute_data)) != 0) {
58 if (ret > 0) {
59 execute_data = EG(current_execute_data);
60 } else {
61 return;
62 }
63 }
64 }
65}
66
67static zend_op_array *(*orig_compile_string)(
68 zend_string *source_string, const char *filename, zend_compile_position position);
69
70static zend_op_array *fuzzer_compile_string(
71 zend_string *str, const char *filename, zend_compile_position position) {
72 if (ZSTR_LEN(str) > MAX_SIZE) {
73 /* Avoid compiling huge inputs via eval(). */
74 fuzzer_bailout();
75 }
76
77 return orig_compile_string(str, filename, position);
78}
79
80static void (*orig_execute_internal)(zend_execute_data *execute_data, zval *return_value);
81
82static void fuzzer_execute_internal(zend_execute_data *execute_data, zval *return_value) {
83 fuzzer_step();
84
86 for (uint32_t i = 0; i < num_args; i++) {
87 /* Some internal functions like preg_replace() may be slow on large inputs.
88 * Limit the maximum size of string inputs. */
91 fuzzer_bailout();
92 }
93 }
94
95 orig_execute_internal(execute_data, return_value);
96}
97
98static void fuzzer_init_php_for_execute(const char *extra_ini) {
99 /* Compilation will often trigger fatal errors.
100 * Use tracked allocation mode to avoid leaks in that case. */
101 putenv("USE_TRACKED_ALLOC=1");
102
103 /* Just like other SAPIs, ignore SIGPIPEs. */
104 signal(SIGPIPE, SIG_IGN);
105
106 fuzzer_init_php(extra_ini);
107
108 orig_execute_ex = zend_execute_ex;
109 zend_execute_ex = fuzzer_execute_ex;
111 zend_execute_internal = fuzzer_execute_internal;
112 orig_compile_string = zend_compile_string;
113 zend_compile_string = fuzzer_compile_string;
114}
115
116ZEND_ATTRIBUTE_UNUSED static void create_file(void) {
117 /* For opcache_invalidate() to work, the dummy file name used for fuzzing needs to
118 * actually exist. */
119 FILE *f = fopen(FILE_NAME, "w");
120 fclose(f);
121}
122
124 steps_left = MAX_STEPS;
126 zval retval, func, args[2];
127 ZVAL_STRING(&func, "opcache_invalidate");
129 ZVAL_TRUE(&args[1]);
130 call_user_function(CG(function_table), NULL, &func, &retval, 2, args);
132 zval_ptr_dtor(&args[0]);
136}
137
139 /* Try relative to cwd. */
140 char *p = realpath("modules/opcache.so", NULL);
141 if (p) {
142 return p;
143 }
144
145 /* Try relative to binary location. */
146 char path[MAXPATHLEN];
147#if defined(__FreeBSD__)
148 size_t pathlen = sizeof(path);
149 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
150 if (sysctl(mib, 4, path, &pathlen, NULL, 0) < 0) {
151#else
152 if (readlink("/proc/self/exe", path, sizeof(path)) < 0) {
153#endif
154 ZEND_ASSERT(0 && "Failed to get binary path");
155 return NULL;
156 }
157
158 /* Get basename. */
159 char *last_sep = strrchr(path, '/');
160 if (last_sep) {
161 *last_sep = '\0';
162 }
163
164 strlcat(path, "/modules/opcache.so", sizeof(path));
165 return realpath(path, NULL);
166}
readlink(string $path)
putenv(string $assignment)
strrchr(string $haystack, string $needle, bool $before_needle=false)
fclose($stream)
fopen(string $filename, string $mode, bool $use_include_path=false, $context=null)
realpath(string $path)
zval * arg
Definition ffi.c:3975
#define FILE_NAME
Definition file.h:267
#define MAX_SIZE
ZEND_ATTRIBUTE_UNUSED char * get_opcache_path(void)
#define MAX_STEPS
int fuzzer_init_php(const char *extra_ini)
#define NULL
Definition gdcache.h:45
opcache_invalidate(string $filename, bool $force=false)
const SIG_IGN
const SIGPIPE
#define strlcat
Definition php.h:169
p
Definition session.c:1105
#define zend_bailout()
Definition zend.h:268
#define ZVAL_STRING(z, s)
Definition zend_API.h:956
#define call_user_function(function_table, object, function_name, retval_ptr, param_count, params)
Definition zend_API.h:687
struct _zval_struct zval
uint32_t num_args
execute_data func
zval * args
ZEND_API zend_op_array *(* zend_compile_string)(zend_string *source_string, const char *filename, zend_compile_position position)
enum _zend_compile_position zend_compile_position
#define EX(element)
struct _zend_op_array zend_op_array
#define ZEND_CALL_NUM_ARGS(call)
#define ZEND_CALL_VAR_NUM(call, n)
void zend_exception_restore(void)
void zend_exception_save(void)
ZEND_API void execute_internal(zend_execute_data *execute_data, zval *return_value)
ZEND_API void(ZEND_FASTCALL *zend_touch_vm_stack_data)(void *vm_stack_data)
ZEND_API void(* zend_execute_ex)(zend_execute_data *execute_data)
ZEND_API void(* zend_execute_internal)(zend_execute_data *execute_data, zval *return_value)
#define CG(v)
#define EG(v)
struct _zend_string zend_string
#define ZEND_ATTRIBUTE_UNUSED
#define zend_always_inline
#define ZEND_FASTCALL
#define ZEND_ASSERT(c)
#define ZSTR_LEN(zstr)
Definition zend_string.h:69
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define IS_TRUE
Definition zend_types.h:603
#define ZVAL_TRUE(z)
#define IS_STRING
Definition zend_types.h:606
#define Z_STRLEN_P(zval_p)
Definition zend_types.h:978
#define Z_TYPE(zval)
Definition zend_types.h:659
struct _zend_execute_data zend_execute_data
Definition zend_types.h:91
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
#define MAXPATHLEN
zval retval
zval * return_value
fbc internal_function handler(call, ret)
execute_data
zval * ret
ZEND_OPCODE_HANDLER_RET(ZEND_FASTCALL * opcode_handler_t)(ZEND_OPCODE_HANDLER_ARGS)