php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
fuzzer-sapi.c
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: Johannes Schlüter <johanes@php.net> |
14 | Stanislav Malyshev <stas@php.net> |
15 +----------------------------------------------------------------------+
16 */
17
18#include <main/php.h>
19#include <main/php_main.h>
20#include <main/SAPI.h>
21#include <ext/standard/info.h>
23#include <main/php_variables.h>
24#include <zend_exceptions.h>
25
26#ifdef __SANITIZE_ADDRESS__
27# include "sanitizer/lsan_interface.h"
28#endif
29
30#include "fuzzer.h"
31#include "fuzzer-sapi.h"
32
33static const char HARDCODED_INI[] =
34 "html_errors=0\n"
35 "implicit_flush=1\n"
36 "output_buffering=0\n"
37 "error_reporting=0\n"
38 /* Let the timeout be enforced by libfuzzer, not PHP. */
39 "max_execution_time=0\n"
40 /* Reduce oniguruma limits to speed up fuzzing */
41 "mbstring.regex_stack_limit=10000\n"
42 "mbstring.regex_retry_limit=10000\n"
43 /* For the "execute" fuzzer disable some functions that are likely to have
44 * undesirable consequences (shell execution, file system writes). */
45 "allow_url_include=0\n"
46 "allow_url_fopen=0\n"
47 "open_basedir=/tmp\n"
48 "disable_functions=dl,mail,mb_send_mail"
49 ",shell_exec,exec,system,proc_open,popen,passthru,pcntl_exec"
50 ",chdir,chgrp,chmod,chown,copy,file_put_contents,lchgrp,lchown,link,mkdir"
51 ",move_uploaded_file,rename,rmdir,symlink,tempname,touch,unlink,fopen"
52 /* Networking code likes to wait and wait. */
53 ",fsockopen,pfsockopen"
54 ",stream_socket_pair,stream_socket_client,stream_socket_server"
55 /* crypt() can be very slow. */
56 ",crypt"
57 /* openlog() has a known memory-management issue. */
58 ",openlog"
59 /* Can cause long loops that bypass the executor step limit. */
60 "\ndisable_classes=InfiniteIterator"
61;
62
63static int startup(sapi_module_struct *sapi_module)
64{
66}
67
68static size_t ub_write(const char *str, size_t str_length)
69{
70 /* quiet */
71 return str_length;
72}
73
74static void fuzzer_flush(void *server_context)
75{
76 /* quiet */
77}
78
79static void send_header(sapi_header_struct *sapi_header, void *server_context)
80{
81}
82
83static char* read_cookies(void)
84{
85 /* TODO: fuzz these! */
86 return NULL;
87}
88
89static void register_variables(zval *track_vars_array)
90{
91 php_import_environment_variables(track_vars_array);
92}
93
94static void log_message(const char *message, int level)
95{
96}
97
98
99static sapi_module_struct fuzzer_module = {
100 "fuzzer", /* name */
101 "clang fuzzer", /* pretty name */
102
103 startup, /* startup */
104 php_module_shutdown_wrapper, /* shutdown */
105
106 NULL, /* activate */
107 NULL, /* deactivate */
108
109 ub_write, /* unbuffered write */
110 fuzzer_flush, /* flush */
111 NULL, /* get uid */
112 NULL, /* getenv */
113
114 php_error, /* error handler */
115
116 NULL, /* header handler */
117 NULL, /* send headers handler */
118 send_header, /* send header handler */
119
120 NULL, /* read POST data */
121 read_cookies, /* read Cookies */
122
123 register_variables, /* register server variables */
124 log_message, /* Log message */
125 NULL, /* Get request time */
126 NULL, /* Child terminate */
127
129};
130
131int fuzzer_init_php(const char *extra_ini)
132{
133#ifdef __SANITIZE_ADDRESS__
134 /* We're going to leak all the memory allocated during startup,
135 * so disable lsan temporarily. */
136 __lsan_disable();
137#endif
138
139 sapi_startup(&fuzzer_module);
140 fuzzer_module.phpinfo_as_text = 1;
141
142 size_t ini_len = sizeof(HARDCODED_INI);
143 size_t extra_ini_len = extra_ini ? strlen(extra_ini) : 0;
144 if (extra_ini) {
145 ini_len += extra_ini_len + 1;
146 }
147 char *p = malloc(ini_len + 1);
148 fuzzer_module.ini_entries = p;
149 p = zend_mempcpy(p, HARDCODED_INI, sizeof(HARDCODED_INI) - 1);
150 if (extra_ini) {
151 *p++ = '\n';
152 p = zend_mempcpy(p, extra_ini, extra_ini_len);
153 }
154 *p = '\0';
155
156 /*
157 * TODO: we might want to test both Zend and malloc MM, but testing with malloc
158 * is more likely to find bugs, so use that for now.
159 */
160 putenv("USE_ZEND_ALLOC=0");
161
162 if (fuzzer_module.startup(&fuzzer_module)==FAILURE) {
163 return FAILURE;
164 }
165
166#ifdef __SANITIZE_ADDRESS__
167 __lsan_enable();
168#endif
169
170 return SUCCESS;
171}
172
174{
175 if (php_request_startup() == FAILURE) {
177 return FAILURE;
178 }
179
180#ifdef ZEND_SIGNALS
181 /* Some signal handlers will be overridden,
182 * don't complain about them during shutdown. */
183 SIGG(check) = 0;
184#endif
185
186 return SUCCESS;
187}
188
190{
191 zend_try {
192 /* Destroy thrown exceptions. This does not happen as part of request shutdown. */
193 if (EG(exception)) {
194 zend_object_release(EG(exception));
195 EG(exception) = NULL;
196 }
197
198 /* Some fuzzers (like unserialize) may create circular structures. Make sure we free them.
199 * Two calls are performed to handle objects with destructors. */
202 } zend_end_try();
203
205}
206
207/* Set up a dummy stack frame so that exceptions may be thrown. */
209{
211 static zend_function func;
212
214 memset(&func, 0, sizeof(zend_function));
215
217 func.common.function_name = ZSTR_EMPTY_ALLOC();
218 execute_data.func = &func;
219 EG(current_execute_data) = &execute_data;
220}
221
222void fuzzer_set_ini_file(const char *file)
223{
224 if (fuzzer_module.php_ini_path_override) {
225 free(fuzzer_module.php_ini_path_override);
226 }
227 fuzzer_module.php_ini_path_override = strdup(file);
228}
229
230
232{
235
236 free((void *)fuzzer_module.ini_entries);
237 return SUCCESS;
238}
239
241 char *filename, const char *data, size_t data_len, bool execute,
242 void (*before_shutdown)(void))
243{
244 int retval = FAILURE; /* failure by default */
245
247 SG(request_info).argc=0;
248 SG(request_info).argv=NULL;
249
251 return FAILURE;
252 }
253
254 // Commented out to avoid leaking the header callback.
255 //SG(headers_sent) = 1;
256 //SG(request_info).no_headers = 1;
257 php_register_variable("PHP_SELF", filename, NULL);
258
260 zend_file_handle file_handle;
261 zend_stream_init_filename(&file_handle, filename);
262 file_handle.primary_script = 1;
263 file_handle.buf = emalloc(data_len + ZEND_MMAP_AHEAD);
264 memcpy(file_handle.buf, data, data_len);
265 memset(file_handle.buf + data_len, 0, ZEND_MMAP_AHEAD);
266 file_handle.len = data_len;
267 /* Avoid ZEND_HANDLE_FILENAME for opcache. */
268 file_handle.type = ZEND_HANDLE_STREAM;
269
270 zend_op_array *op_array = zend_compile_file(&file_handle, ZEND_REQUIRE);
271 zend_destroy_file_handle(&file_handle);
272 if (op_array) {
273 if (execute) {
274 zend_execute(op_array, NULL);
275 }
276 zend_destroy_static_vars(op_array);
277 destroy_op_array(op_array);
278 efree(op_array);
279 }
280 } zend_end_try();
281
282 CG(compiled_filename) = NULL; /* ??? */
283 if (before_shutdown) {
284 zend_try {
285 before_shutdown();
286 } zend_end_try();
287 }
289
290 return (retval == SUCCESS) ? SUCCESS : FAILURE;
291}
292
293// Call named PHP function with N zval arguments
294void fuzzer_call_php_func_zval(const char *func_name, int nargs, zval *args) {
296
299 call_user_function(CG(function_table), NULL, &func, &retval, nargs, args);
300
301 // TODO: check result?
302 /* to ensure retval is not broken */
303 php_var_dump(&retval, 0);
304
305 /* cleanup */
308}
309
310// Call named PHP function with N string arguments
311void fuzzer_call_php_func(const char *func_name, int nargs, char **params) {
312 zval args[nargs];
313 int i;
314
315 for(i=0;i<nargs;i++) {
316 ZVAL_STRING(&args[i], params[i]);
317 }
318
320
321 for(i=0;i<nargs;i++) {
322 zval_ptr_dtor(&args[i]);
323 ZVAL_UNDEF(&args[i]);
324 }
325}
SAPI_API sapi_module_struct sapi_module
Definition SAPI.c:65
SAPI_API void sapi_startup(sapi_module_struct *sf)
Definition SAPI.c:68
SAPI_API void sapi_shutdown(void)
Definition SAPI.c:89
#define SAPI_OPTION_NO_CHDIR
Definition SAPI.h:27
#define SG(v)
Definition SAPI.h:160
#define STANDARD_SAPI_MODULE_PROPERTIES
Definition SAPI.h:324
struct _sapi_module_struct sapi_module_struct
Definition SAPI.h:60
bool exception
Definition assert.c:30
putenv(string $assignment)
file(string $filename, int $flags=0, $context=null)
memcpy(ptr1, ptr2, size)
memset(ptr, 0, type->size)
int fuzzer_request_startup(void)
void fuzzer_set_ini_file(const char *file)
void fuzzer_call_php_func(const char *func_name, int nargs, char **params)
void fuzzer_setup_dummy_frame(void)
int fuzzer_do_request_from_buffer(char *filename, const char *data, size_t data_len, bool execute, void(*before_shutdown)(void))
void fuzzer_call_php_func_zval(const char *func_name, int nargs, zval *args)
void fuzzer_request_shutdown(void)
int fuzzer_shutdown_php(void)
int fuzzer_init_php(const char *extra_ini)
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
void php_request_shutdown(void *dummy)
Definition main.c:1885
zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_module)
Definition main.c:2103
void php_module_shutdown(void)
Definition main.c:2424
zend_result php_request_startup(void)
Definition main.c:1801
int php_module_shutdown_wrapper(sapi_module_struct *sapi_globals)
Definition main.c:2416
#define php_error
Definition php.h:310
PHP_JSON_API size_t int options
Definition php_json.h:102
PHPAPI void php_var_dump(zval *struc, int level)
Definition var.c:103
PHPAPI void(* php_import_environment_variables)(zval *array_ptr)
PHPAPI void php_register_variable(const char *var, const char *strval, zval *track_vars_array)
zend_constant * data
const char * func_name
p
Definition session.c:1105
#define zend_first_try
Definition zend.h:284
#define zend_try
Definition zend.h:270
#define zend_end_try()
Definition zend.h:280
#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
#define efree(ptr)
Definition zend_alloc.h:155
#define emalloc(size)
Definition zend_alloc.h:151
struct _zval_struct zval
strlen(string $string)
execute_data func
zval * args
ZEND_API zend_op_array *(* zend_compile_file)(zend_file_handle *file_handle, int type)
ZEND_API void destroy_op_array(zend_op_array *op_array)
#define ZEND_INTERNAL_FUNCTION
#define ZEND_REQUIRE
struct _zend_op_array zend_op_array
ZEND_API void zend_destroy_static_vars(zend_op_array *op_array)
ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle)
ZEND_API void zend_execute(zend_op_array *op_array, zval *return_value)
union _zend_function zend_function
ZEND_API int zend_gc_collect_cycles(void)
Definition zend_gc.c:1913
#define CG(v)
#define EG(v)
struct _zend_file_handle zend_file_handle
ZEND_API void zend_stream_init_filename(zend_file_handle *handle, const char *filename)
Definition zend_stream.c:70
@ ZEND_HANDLE_STREAM
Definition zend_stream.h:42
#define ZEND_MMAP_AHEAD
Definition zend_stream.h:37
#define ZSTR_EMPTY_ALLOC()
#define ZVAL_UNDEF(z)
@ FAILURE
Definition zend_types.h:61
struct _zend_execute_data zend_execute_data
Definition zend_types.h:91
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
zval retval
execute_data