/src/php-src/sapi/fuzzer/fuzzer-sapi.c
Line | Count | Source (jump to first uncovered line) |
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 | | | http://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> |
22 | | #include <ext/standard/php_var.h> |
23 | | #include <main/php_variables.h> |
24 | | |
25 | | #ifdef __SANITIZE_ADDRESS__ |
26 | | # include "sanitizer/lsan_interface.h" |
27 | | #endif |
28 | | |
29 | | #include "fuzzer.h" |
30 | | #include "fuzzer-sapi.h" |
31 | | |
32 | | const char HARDCODED_INI[] = |
33 | | "html_errors=0\n" |
34 | | "implicit_flush=1\n" |
35 | | "output_buffering=0\n" |
36 | | "error_reporting=0\n" |
37 | | /* Reduce oniguruma limits to speed up fuzzing */ |
38 | | "mbstring.regex_stack_limit=10000\n" |
39 | | "mbstring.regex_retry_limit=10000"; |
40 | | |
41 | | static int startup(sapi_module_struct *sapi_module) |
42 | 3.68k | { |
43 | 3.68k | if (php_module_startup(sapi_module, NULL, 0)==FAILURE) { |
44 | 0 | return FAILURE; |
45 | 0 | } |
46 | 3.68k | return SUCCESS; |
47 | 3.68k | } |
48 | | |
49 | | static size_t ub_write(const char *str, size_t str_length) |
50 | 0 | { |
51 | | /* quiet */ |
52 | 0 | return str_length; |
53 | 0 | } |
54 | | |
55 | | static void fuzzer_flush(void *server_context) |
56 | 0 | { |
57 | | /* quiet */ |
58 | 0 | } |
59 | | |
60 | | static void send_header(sapi_header_struct *sapi_header, void *server_context) |
61 | 0 | { |
62 | 0 | } |
63 | | |
64 | | static char* read_cookies() |
65 | 0 | { |
66 | | /* TODO: fuzz these! */ |
67 | 0 | return NULL; |
68 | 0 | } |
69 | | |
70 | | static void register_variables(zval *track_vars_array) |
71 | 375 | { |
72 | 375 | php_import_environment_variables(track_vars_array); |
73 | 375 | } |
74 | | |
75 | | static void log_message(const char *message, int level) |
76 | 0 | { |
77 | 0 | } |
78 | | |
79 | | |
80 | | static sapi_module_struct fuzzer_module = { |
81 | | "fuzzer", /* name */ |
82 | | "clang fuzzer", /* pretty name */ |
83 | | |
84 | | startup, /* startup */ |
85 | | php_module_shutdown_wrapper, /* shutdown */ |
86 | | |
87 | | NULL, /* activate */ |
88 | | NULL, /* deactivate */ |
89 | | |
90 | | ub_write, /* unbuffered write */ |
91 | | fuzzer_flush, /* flush */ |
92 | | NULL, /* get uid */ |
93 | | NULL, /* getenv */ |
94 | | |
95 | | php_error, /* error handler */ |
96 | | |
97 | | NULL, /* header handler */ |
98 | | NULL, /* send headers handler */ |
99 | | send_header, /* send header handler */ |
100 | | |
101 | | NULL, /* read POST data */ |
102 | | read_cookies, /* read Cookies */ |
103 | | |
104 | | register_variables, /* register server variables */ |
105 | | log_message, /* Log message */ |
106 | | NULL, /* Get request time */ |
107 | | NULL, /* Child terminate */ |
108 | | |
109 | | STANDARD_SAPI_MODULE_PROPERTIES |
110 | | }; |
111 | | |
112 | | int fuzzer_init_php() |
113 | 3.68k | { |
114 | | #ifdef __SANITIZE_ADDRESS__ |
115 | | /* We're going to leak all the memory allocated during startup, |
116 | | * so disable lsan temporarily. */ |
117 | | __lsan_disable(); |
118 | | #endif |
119 | | |
120 | 3.68k | sapi_startup(&fuzzer_module); |
121 | 3.68k | fuzzer_module.phpinfo_as_text = 1; |
122 | | |
123 | 3.68k | fuzzer_module.ini_entries = malloc(sizeof(HARDCODED_INI)); |
124 | 3.68k | memcpy(fuzzer_module.ini_entries, HARDCODED_INI, sizeof(HARDCODED_INI)); |
125 | | |
126 | | /* |
127 | | * TODO: we might want to test both Zend and malloc MM, but testing with malloc |
128 | | * is more likely to find bugs, so use that for now. |
129 | | */ |
130 | 3.68k | putenv("USE_ZEND_ALLOC=0"); |
131 | | |
132 | 3.68k | if (fuzzer_module.startup(&fuzzer_module)==FAILURE) { |
133 | 0 | return FAILURE; |
134 | 0 | } |
135 | | |
136 | | #ifdef __SANITIZE_ADDRESS__ |
137 | | __lsan_enable(); |
138 | | #endif |
139 | | |
140 | 3.68k | return SUCCESS; |
141 | 3.68k | } |
142 | | |
143 | | int fuzzer_request_startup() |
144 | 391k | { |
145 | 391k | if (php_request_startup() == FAILURE) { |
146 | 0 | php_module_shutdown(); |
147 | 0 | return FAILURE; |
148 | 0 | } |
149 | | |
150 | 391k | #ifdef ZEND_SIGNALS |
151 | | /* Some signal handlers will be overridden, |
152 | | * don't complain about them during shutdown. */ |
153 | 391k | SIGG(check) = 0; |
154 | 391k | #endif |
155 | | |
156 | 391k | return SUCCESS; |
157 | 391k | } |
158 | | |
159 | | void fuzzer_request_shutdown() |
160 | 0 | { |
161 | | /* Destroy thrown exceptions. This does not happen as part of request shutdown. */ |
162 | 0 | if (EG(exception)) { |
163 | 0 | zend_object_release(EG(exception)); |
164 | 0 | EG(exception) = NULL; |
165 | 0 | } |
166 | | |
167 | | /* Some fuzzers (like unserialize) may create circular structures. Make sure we free them. |
168 | | * Two calls are performed to handle objects with destructors. */ |
169 | 0 | zend_gc_collect_cycles(); |
170 | 0 | zend_gc_collect_cycles(); |
171 | |
|
172 | 0 | php_request_shutdown(NULL); |
173 | 0 | } |
174 | | |
175 | | /* Set up a dummy stack frame so that exceptions may be thrown. */ |
176 | | void fuzzer_setup_dummy_frame() |
177 | 0 | { |
178 | 0 | static zend_execute_data execute_data; |
179 | 0 | static zend_function func; |
180 | |
|
181 | 0 | memset(&execute_data, 0, sizeof(zend_execute_data)); |
182 | 0 | memset(&func, 0, sizeof(zend_function)); |
183 | |
|
184 | 0 | func.type = ZEND_INTERNAL_FUNCTION; |
185 | 0 | func.common.function_name = ZSTR_EMPTY_ALLOC(); |
186 | 0 | execute_data.func = &func; |
187 | 0 | EG(current_execute_data) = &execute_data; |
188 | 0 | } |
189 | | |
190 | | void fuzzer_set_ini_file(const char *file) |
191 | 0 | { |
192 | 0 | if (fuzzer_module.php_ini_path_override) { |
193 | 0 | free(fuzzer_module.php_ini_path_override); |
194 | 0 | } |
195 | 0 | fuzzer_module.php_ini_path_override = strdup(file); |
196 | 0 | } |
197 | | |
198 | | |
199 | | int fuzzer_shutdown_php() |
200 | 0 | { |
201 | 0 | php_module_shutdown(); |
202 | 0 | sapi_shutdown(); |
203 | |
|
204 | 0 | free(fuzzer_module.ini_entries); |
205 | 0 | return SUCCESS; |
206 | 0 | } |
207 | | |
208 | | int fuzzer_do_request(zend_file_handle *file_handle, char *filename) |
209 | 391k | { |
210 | 391k | int retval = FAILURE; /* failure by default */ |
211 | | |
212 | 391k | SG(options) |= SAPI_OPTION_NO_CHDIR; |
213 | 391k | SG(request_info).argc=0; |
214 | 391k | SG(request_info).argv=NULL; |
215 | | |
216 | 391k | if (fuzzer_request_startup() == FAILURE) { |
217 | 0 | return FAILURE; |
218 | 0 | } |
219 | | |
220 | 391k | SG(headers_sent) = 1; |
221 | 391k | SG(request_info).no_headers = 1; |
222 | 391k | php_register_variable("PHP_SELF", filename, NULL); |
223 | | |
224 | 391k | zend_first_try { |
225 | 391k | zend_op_array *op_array = zend_compile_file(file_handle, ZEND_REQUIRE); |
226 | 391k | if (op_array) { |
227 | 140k | destroy_op_array(op_array); |
228 | 140k | efree(op_array); |
229 | 140k | } |
230 | 391k | if (EG(exception)) { |
231 | 233k | zend_object_release(EG(exception)); |
232 | 233k | EG(exception) = NULL; |
233 | 233k | } |
234 | | /*retval = php_execute_script(file_handle);*/ |
235 | 391k | } zend_end_try(); |
236 | | |
237 | 391k | php_request_shutdown((void *) 0); |
238 | | |
239 | 391k | return (retval == SUCCESS) ? SUCCESS : FAILURE; |
240 | 391k | } |
241 | | |
242 | | |
243 | | int fuzzer_do_request_f(char *filename) |
244 | 0 | { |
245 | 0 | zend_file_handle file_handle; |
246 | 0 | file_handle.type = ZEND_HANDLE_FILENAME; |
247 | 0 | file_handle.filename = filename; |
248 | 0 | file_handle.handle.fp = NULL; |
249 | 0 | file_handle.opened_path = NULL; |
250 | |
|
251 | 0 | return fuzzer_do_request(&file_handle, filename); |
252 | 0 | } |
253 | | |
254 | | int fuzzer_do_request_from_buffer(char *filename, char *data, size_t data_len) |
255 | 391k | { |
256 | 391k | zend_file_handle file_handle; |
257 | 391k | file_handle.filename = filename; |
258 | 391k | file_handle.free_filename = 0; |
259 | 391k | file_handle.opened_path = NULL; |
260 | 391k | file_handle.handle.stream.handle = NULL; |
261 | 391k | file_handle.handle.stream.reader = (zend_stream_reader_t)_php_stream_read; |
262 | 391k | file_handle.handle.stream.fsizer = NULL; |
263 | 391k | file_handle.handle.stream.isatty = 0; |
264 | 391k | file_handle.handle.stream.closer = NULL; |
265 | 391k | file_handle.buf = data; |
266 | 391k | file_handle.len = data_len; |
267 | 391k | file_handle.type = ZEND_HANDLE_STREAM; |
268 | | |
269 | 391k | return fuzzer_do_request(&file_handle, filename); |
270 | 391k | } |
271 | | |
272 | | // Call named PHP function with N zval arguments |
273 | 0 | void fuzzer_call_php_func_zval(const char *func_name, int nargs, zval *args) { |
274 | 0 | zval retval, func; |
275 | |
|
276 | 0 | ZVAL_STRING(&func, func_name); |
277 | 0 | ZVAL_UNDEF(&retval); |
278 | 0 | call_user_function(CG(function_table), NULL, &func, &retval, nargs, args); |
279 | | |
280 | | // TODO: check result? |
281 | | /* to ensure retval is not broken */ |
282 | 0 | php_var_dump(&retval, 0); |
283 | | |
284 | | /* cleanup */ |
285 | 0 | zval_ptr_dtor(&retval); |
286 | 0 | zval_ptr_dtor(&func); |
287 | 0 | } |
288 | | |
289 | | // Call named PHP function with N string arguments |
290 | 0 | void fuzzer_call_php_func(const char *func_name, int nargs, char **params) { |
291 | 0 | zval args[nargs]; |
292 | 0 | int i; |
293 | |
|
294 | 0 | for(i=0;i<nargs;i++) { |
295 | 0 | ZVAL_STRING(&args[i], params[i]); |
296 | 0 | } |
297 | |
|
298 | 0 | fuzzer_call_php_func_zval(func_name, nargs, args); |
299 | |
|
300 | 0 | for(i=0;i<nargs;i++) { |
301 | 0 | zval_ptr_dtor(&args[i]); |
302 | 0 | ZVAL_UNDEF(&args[i]); |
303 | 0 | } |
304 | 0 | } |