improved type validation
[m6w6/ext-psi] / src / call.c
1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *******************************************************************************/
25
26 #include "php_psi_stdinc.h"
27 #include "context.h"
28 #include "data.h"
29 #include "call.h"
30
31 #include "php.h"
32 #include "zend_exceptions.h"
33 #include "ext/spl/spl_exceptions.h"
34
35 struct psi_call_frame_argument *psi_call_frame_argument_init(struct psi_impl_arg *spec,
36 impl_val *ival, zval *zptr, int is_vararg) {
37 struct psi_call_frame_argument *frame_arg = ecalloc(1, sizeof(*frame_arg));
38
39
40 if ((frame_arg->zval_ptr = zptr)) {
41 ZVAL_DEREF(frame_arg->zval_ptr);
42
43 /* use userland type if the declared type of the vararg is mixed */
44 if (is_vararg) {
45 if (spec->type->type == PSI_T_MIXED) {
46 switch (Z_TYPE_P(zptr)) {
47 case IS_TRUE:
48 frame_arg->va_type = PSI_T_BOOL;
49 ival->zend.bval = 1;
50 break;
51 case IS_FALSE:
52 frame_arg->va_type = PSI_T_BOOL;
53 ival->zend.bval = 0;
54 break;
55 case IS_LONG:
56 frame_arg->va_type = PSI_T_INT;
57 ival->zend.lval = Z_LVAL_P(zptr);
58 break;
59 case IS_DOUBLE:
60 frame_arg->va_type = PSI_T_FLOAT;
61 ival->dval = Z_DVAL_P(zptr);
62 break;
63 default:
64 frame_arg->va_type = PSI_T_STRING;
65 ival->zend.str = zval_get_string(zptr);
66 break;
67 }
68 } else {
69 frame_arg->va_type = spec->type->type;
70 }
71 }
72 }
73
74 frame_arg->ival = *ival;
75 frame_arg->ival_ptr = &frame_arg->ival;
76 frame_arg->spec = spec;
77
78 return frame_arg;
79 }
80
81 void psi_call_frame_argument_free(struct psi_call_frame_argument *arg) {
82 switch (arg->spec->type->type) {
83 case PSI_T_STRING:
84 if (arg->ival.zend.str) {
85 zend_string_release(arg->ival.zend.str);
86 }
87 break;
88 case PSI_T_CALLABLE:
89 if (arg->ival.zend.cb) {
90 if (arg->ival.zend.cb->fci.size) {
91 zend_fcall_info_args_clear(&arg->ival.zend.cb->fci, 1);
92 }
93 efree(arg->ival.zend.cb);
94 }
95 break;
96 default:
97 break;
98 }
99 if (arg->ival_ptr && arg->ival_ptr != &arg->temp_val && arg->ival_ptr != &arg->ival) {
100 efree(arg->ival_ptr);
101 }
102 efree(arg);
103 }
104
105 struct psi_call_frame_symbol *psi_call_frame_symbol_init(struct psi_decl_var *dvar) {
106 struct psi_call_frame_symbol *frame_sym;
107 size_t size = psi_decl_type_get_size(dvar->arg->type, NULL);
108
109 frame_sym = ecalloc(1, sizeof(*frame_sym) + size);
110 frame_sym->ptr = frame_sym->ival_ptr = &frame_sym->temp_val;
111
112 return frame_sym;
113 }
114
115 void psi_call_frame_symbol_free(struct psi_call_frame_symbol *sym) {
116 efree(sym);
117 }
118
119 static void psi_call_frame_free_argument(zval *zptr) {
120 psi_call_frame_argument_free(Z_PTR_P(zptr));
121 }
122
123 static void psi_call_frame_free_symbol(zval *zptr) {
124 psi_call_frame_symbol_free(Z_PTR_P(zptr));
125 }
126
127 struct psi_call_frame_auto_free {
128 void *data;
129 void (*dtor)(void *);
130 };
131
132 static void psi_call_frame_free_temp(void *ptr) {
133 struct psi_call_frame_auto_free *f = ptr;
134
135 if (f->data) {
136 if (f->dtor) {
137 f->dtor(&f->data);
138 } else {
139 efree(f->data);
140 }
141 }
142 }
143
144 struct psi_call_frame *psi_call_frame_init(struct psi_context *C, struct psi_decl *decl, struct psi_impl *impl) {
145 struct psi_call_frame *frame = ecalloc(1, sizeof(*frame));
146
147 frame->context = C;
148 frame->decl = decl;
149 frame->impl = impl;
150
151 zend_hash_init(&frame->arguments, 8, NULL, psi_call_frame_free_argument, 0);
152 zend_hash_init(&frame->symbols, 8, NULL, psi_call_frame_free_symbol, 0);
153 zend_llist_init(&frame->temp, sizeof(struct psi_call_frame_auto_free), psi_call_frame_free_temp, 0);
154
155 return frame;
156 }
157
158 struct psi_call_frame_symbol *psi_call_frame_fetch_symbol(
159 struct psi_call_frame *frame, struct psi_decl_var *dvar) {
160 struct psi_call_frame_symbol *frame_sym;
161
162 frame_sym = zend_hash_str_find_ptr(&frame->symbols, dvar->fqn, strlen(dvar->fqn));
163 if (!frame_sym) {
164 frame_sym = zend_hash_str_add_ptr(&frame->symbols, dvar->fqn, strlen(dvar->fqn),
165 psi_call_frame_symbol_init(dvar));
166 }
167 return frame_sym;
168 }
169
170 zval *psi_call_frame_new_argument(struct psi_call_frame *frame,
171 struct psi_call_frame_argument *frame_arg) {
172 if (frame_arg->va_type) {
173 /* varargs are just appended with numeric indices */
174 return zend_hash_next_index_insert_ptr(&frame->arguments, frame_arg);
175 } else {
176 return zend_hash_str_add_ptr(&frame->arguments,
177 frame_arg->spec->var->name, strlen(frame_arg->spec->var->name),
178 frame_arg);
179 }
180 }
181
182 zval *psi_call_frame_sub_argument(struct psi_call_frame *frame,
183 struct psi_impl_var *inner_var, zval *outer_zval, const char *name) {
184 struct psi_call_frame_argument *iarg;
185 zval *inner_zval = zend_symtable_str_find(Z_ARRVAL_P(outer_zval),
186 &inner_var->name[1], strlen(&inner_var->name[1]));
187
188 if (!inner_zval) {
189 zval empty_zval;
190
191 SEPARATE_ZVAL(outer_zval);
192 ZVAL_NULL(&empty_zval);
193 inner_zval = zend_symtable_str_update(Z_ARRVAL_P(outer_zval),
194 &inner_var->name[1], strlen(&inner_var->name[1]),
195 &empty_zval);
196 }
197
198 iarg = psi_call_frame_get_argument(frame, name);
199
200 if (!iarg) {
201 struct psi_call_frame_argument *frame_arg;
202 impl_val empty_val = {0};
203 struct psi_impl_arg *carg_spec = psi_impl_arg_init(
204 psi_impl_type_init(PSI_T_MIXED, "mixed"),
205 psi_impl_var_copy(inner_var), NULL);
206
207 psi_call_frame_push_auto_ex(frame, carg_spec, (void(*)(void*)) psi_impl_arg_free);
208 frame_arg = psi_call_frame_argument_init(carg_spec, &empty_val, inner_zval, 0);
209 zend_hash_str_add_ptr(&frame->arguments, name, strlen(name), frame_arg);
210 }
211
212 return inner_zval;
213 }
214
215 struct psi_call_frame_argument *psi_call_frame_get_argument(
216 struct psi_call_frame *frame, const char *name) {
217 return zend_hash_str_find_ptr(&frame->arguments, name, strlen(name));
218 }
219
220 size_t psi_call_frame_num_var_args(struct psi_call_frame *frame) {
221 return zend_hash_next_free_element(&frame->arguments);
222 }
223
224 size_t psi_call_frame_num_fixed_args(struct psi_call_frame *frame) {
225 return psi_plist_count(frame->decl->args);
226 }
227
228 struct psi_call_frame_argument *psi_call_frame_get_var_argument(
229 struct psi_call_frame *frame, zend_long index) {
230 return zend_hash_index_find_ptr(&frame->arguments, index);
231 }
232
233 void **psi_call_frame_get_arg_pointers(struct psi_call_frame *frame) {
234 return frame->pointers;
235 }
236
237 ZEND_RESULT_CODE psi_call_frame_parse_args(struct psi_call_frame *frame,
238 zend_execute_data *execute_data) {
239 size_t i, argc = psi_plist_count(frame->impl->func->args);
240 zend_error_handling zeh;
241
242 zend_replace_error_handling(EH_THROW, zend_exception_get_default(), &zeh);
243
244 if (!argc) {
245 ZEND_RESULT_CODE rv;
246
247 rv = zend_parse_parameters_none();
248 zend_restore_error_handling(&zeh);
249 return rv;
250 }
251
252 ZEND_PARSE_PARAMETERS_START(
253 psi_impl_num_min_args(frame->impl),
254 frame->impl->func->vararg ? -1 : argc
255 )
256 nextarg: {
257
258 struct psi_impl_arg *iarg;
259 impl_val ival = {0};
260
261 if (frame->impl->func->vararg && _i >= argc) {
262 iarg = frame->impl->func->vararg;
263 Z_PARAM_OPTIONAL;
264 } else {
265 psi_plist_get(frame->impl->func->args, _i, &iarg);
266 if (iarg->def) {
267 Z_PARAM_OPTIONAL;
268 }
269 }
270
271 if (PSI_T_BOOL == iarg->type->type) {
272 Z_PARAM_BOOL(ival.zend.bval);
273 } else if (PSI_T_INT == iarg->type->type) {
274 Z_PARAM_LONG(ival.zend.lval);
275 } else if (PSI_T_FLOAT == iarg->type->type || PSI_T_DOUBLE == iarg->type->type) {
276 Z_PARAM_DOUBLE(ival.dval);
277 } else if (PSI_T_STRING == iarg->type->type) {
278 Z_PARAM_STR_EX(ival.zend.str, 1, iarg->var->reference);
279 if (ival.zend.str) {
280 zend_string_addref(ival.zend.str);
281 }
282 } else if (PSI_T_ARRAY == iarg->type->type) {
283 zval *tmp;
284 Z_PARAM_ARRAY_EX(tmp, _optional || iarg->var->reference,
285 iarg->var->reference);
286 } else if (PSI_T_OBJECT == iarg->type->type) {
287 Z_PARAM_PROLOGUE(iarg->var->reference);
288 } else if (PSI_T_MIXED == iarg->type->type) {
289 Z_PARAM_PROLOGUE(iarg->var->reference);
290 } else if (PSI_T_CALLABLE == iarg->type->type) {
291 zend_fcall_info fci;
292 zend_fcall_info_cache fcc;
293
294 Z_PARAM_FUNC_EX(fci, fcc, 1, 0);
295
296 if (fci.size) {
297 ival.zend.cb = ecalloc(1, sizeof(zend_fcall));
298 ival.zend.cb->fci = fci;
299 ival.zend.cb->fcc = fcc;
300 }
301 } else {
302 error_code = ZPP_ERROR_FAILURE;
303 break;
304 }
305
306 psi_call_frame_new_argument(frame,
307 psi_call_frame_argument_init(iarg, &ival, _arg, _i > argc));
308
309 if (_i < _num_args) {
310 goto nextarg;
311 }
312 }
313 ZEND_PARSE_PARAMETERS_END_EX(
314 zend_restore_error_handling(&zeh);
315 return FAILURE;
316 );
317
318 /* set up defaults */
319 for (i = EX_NUM_ARGS(); i < argc; ++i) {
320 struct psi_impl_arg *iarg;
321
322 psi_plist_get(frame->impl->func->args, i, &iarg);
323
324 assert(iarg->def);
325 psi_call_frame_new_argument(frame, psi_call_frame_argument_init(iarg,
326 &iarg->def->ival, NULL, 0));
327 }
328
329 zend_restore_error_handling(&zeh);
330 return SUCCESS;
331 }
332
333 void psi_call_frame_enter(struct psi_call_frame *frame) {
334 size_t argc = psi_call_frame_num_fixed_args(frame);
335 size_t va_count = psi_call_frame_num_var_args(frame);
336 size_t rsize = psi_decl_arg_get_size(frame->decl->func);
337 struct psi_call_frame_symbol *rv_sym;
338
339 /* initialize ffi argument array */
340 frame->pointers = ecalloc(argc + va_count + 1, sizeof(void *));
341
342 /* initialize return value symbol */
343 rv_sym = psi_call_frame_fetch_symbol(frame, frame->decl->func->var);
344 if (rsize > sizeof(impl_val)) {
345 rv_sym->ival_ptr = ecalloc(1, rsize);
346 } else {
347 rv_sym->ival_ptr = &rv_sym->temp_val;
348 }
349 frame->rpointer = rv_sym->ptr = rv_sym->ival_ptr;
350 }
351
352 ZEND_RESULT_CODE psi_call_frame_do_let(struct psi_call_frame *frame) {
353 size_t i;
354 struct psi_let_stmt *let;
355 struct psi_decl_arg *arg;
356 size_t argc = psi_call_frame_num_fixed_args(frame);
357 size_t va_count = psi_call_frame_num_var_args(frame);
358
359 for (i = 0; psi_plist_get(frame->impl->stmts.let, i, &let); ++i) {
360 psi_let_stmt_exec(let, frame);
361 }
362 for (i = 0; psi_plist_get(frame->decl->args, i, &arg); ++i) {
363 struct psi_let_stmt *let;
364 struct psi_call_frame_symbol *frame_sym;
365
366 let = psi_impl_get_let(frame->impl, arg->var);
367 frame_sym = psi_call_frame_fetch_symbol(frame, let->exp->var);
368 frame->pointers[i] = frame_sym->ptr;
369 }
370 /* varargs */
371 if (va_count) {
372 for (i = 0; i < va_count; ++i) {
373 struct psi_call_frame_argument *frame_arg;
374 psi_marshal_let let_fn;
375 void *temp = NULL;
376
377 frame_arg = psi_call_frame_get_var_argument(frame, i);
378 switch (frame_arg->va_type) {
379 case PSI_T_BOOL: let_fn = psi_let_boolval; break;
380 case PSI_T_INT: let_fn = psi_let_intval; break;
381 case PSI_T_FLOAT:
382 case PSI_T_DOUBLE: let_fn = psi_let_floatval; break;
383 case PSI_T_STRING: let_fn = psi_let_strval; break;
384 default:
385 assert(0);
386 }
387
388 frame_arg->ival_ptr = let_fn(&frame_arg->temp_val, NULL, frame_arg->va_type,
389 &frame_arg->ival, frame_arg->zval_ptr, &temp);
390 if (temp) {
391 psi_call_frame_push_auto(frame, temp);
392 }
393
394 frame->pointers[argc + i] = frame_arg->ival_ptr;
395 }
396 }
397
398 return SUCCESS;
399 }
400
401 ZEND_RESULT_CODE psi_call_frame_do_assert(struct psi_call_frame *frame, enum psi_assert_kind kind) {
402 size_t i = 0;
403 struct psi_assert_stmt *ass;
404
405 while (psi_plist_get(frame->impl->stmts.ass, i++, &ass)) {
406 if (ass->kind == kind) {
407 if (!psi_assert_stmt_exec(ass, frame)) {
408 char *message = psi_assert_stmt_message(ass);
409 zend_throw_exception(kind == PSI_ASSERT_PRE
410 ? spl_ce_InvalidArgumentException
411 : spl_ce_UnexpectedValueException, message, 0);
412 free(message);
413 return FAILURE;
414 }
415 }
416 }
417
418 return SUCCESS;
419 }
420
421 void psi_call_frame_do_call(struct psi_call_frame *frame) {
422 size_t va_count = psi_call_frame_num_var_args(frame);
423
424 if (va_count) {
425 void **va_types = ecalloc(va_count, sizeof(void *));
426 size_t i;
427
428 for (i = 0; i < va_count; ++i) {
429 struct psi_call_frame_argument *frame_arg;
430
431 frame_arg = psi_call_frame_get_var_argument(frame, i);
432 va_types[i] = frame->context->ops->query(frame->context,
433 PSI_CONTEXT_QUERY_TYPE, &frame_arg->va_type);
434 }
435
436 frame->context->ops->call_va(frame->context,
437 frame,
438 frame->decl,
439 frame->rpointer,
440 frame->pointers,
441 va_count,
442 va_types);
443
444 efree(va_types);
445 } else {
446 frame->context->ops->call(frame->context,
447 frame,
448 frame->decl,
449 frame->rpointer,
450 frame->pointers);
451 }
452 }
453
454 void psi_call_frame_do_callback(struct psi_call_frame *frame, struct psi_call_frame_callback *cbdata)
455 {
456 size_t i;
457 void *retptr;
458 ZEND_RESULT_CODE rc;
459 struct psi_let_callback *cb = cbdata->cb->data.callback;
460 zval return_value, *zargv = ecalloc(cbdata->argc, sizeof(*zargv));
461 struct psi_call_frame_argument *frame_arg;
462
463 assert(cbdata->argc == psi_plist_count(cb->decl->args));
464
465 /* prepare args for the userland call */
466 for (i = 0; i < cbdata->argc; ++i) {
467 struct psi_set_exp *set_exp;
468 struct psi_decl_var *set_var;
469 struct psi_call_frame_symbol *set_sym;
470
471 psi_plist_get(cb->args, i, &set_exp);
472 set_var = psi_set_exp_get_decl_var(set_exp);
473 set_sym = psi_call_frame_fetch_symbol(frame, set_var);
474
475 set_sym->ptr = cbdata->argv[i];
476 psi_set_exp_exec_ex(set_exp, &zargv[i], set_sym->ptr, frame);
477 }
478
479 frame_arg = psi_call_frame_get_argument(frame, cb->func->var->fqn);
480
481 /* callback into userland */
482 ZVAL_UNDEF(&return_value);
483 zend_fcall_info_argp(&frame_arg->ival_ptr->zend.cb->fci, cbdata->argc, zargv);
484 rc = zend_fcall_info_call(&frame_arg->ival_ptr->zend.cb->fci,
485 &frame_arg->ival_ptr->zend.cb->fcc, &return_value, NULL);
486 assert(rc == SUCCESS);
487
488 /* marshal return value of the userland call */
489 frame_arg->zval_ptr = &return_value;
490 retptr = psi_let_func_exec(cbdata->cb, cb->func, cb->decl->func, frame);
491 memcpy(cbdata->rval, retptr, psi_decl_arg_get_size(cb->decl->func));
492
493 /* cleanup */
494 zend_fcall_info_args_clear(&frame_arg->ival_ptr->zend.cb->fci, 0);
495 for (i = 0; i < cbdata->argc; ++i) {
496 zval_ptr_dtor(&zargv[i]);
497 }
498 efree(zargv);
499 }
500
501 void psi_call_frame_do_return(struct psi_call_frame *frame, zval *return_value) {
502 struct psi_return_stmt *ret;
503
504 psi_plist_get(frame->impl->stmts.ret, 0, &ret);
505 psi_return_stmt_exec(ret, return_value, frame);
506 }
507
508 void psi_call_frame_do_set(struct psi_call_frame *frame) {
509 size_t i = 0;
510 struct psi_set_stmt *set;
511
512 while (psi_plist_get(frame->impl->stmts.set, i++, &set)) {
513 psi_set_stmt_exec(set, frame);
514 }
515 }
516
517 void psi_call_frame_do_free(struct psi_call_frame *frame) {
518 size_t i = 0;
519 struct psi_free_stmt *fre;
520
521 while (psi_plist_get(frame->impl->stmts.fre, i++, &fre)) {
522 psi_free_stmt_exec(fre, frame);
523 }
524 }
525
526 void **psi_call_frame_push_auto_ex(struct psi_call_frame *frame, void *auto_free, void (*dtor)(void*)) {
527 struct psi_call_frame_auto_free f;
528
529 f.data = auto_free;
530 f.dtor = dtor;
531
532 zend_llist_add_element(&frame->temp, &f);
533 return &((struct psi_call_frame_auto_free *) zend_llist_get_last(&frame->temp))->data;
534 }
535
536 void **psi_call_frame_push_auto(struct psi_call_frame *frame, void *auto_free) {
537 return psi_call_frame_push_auto_ex(frame, auto_free, NULL);
538 }
539
540 static void psi_call_frame_local_auto_dtor(void *auto_list)
541 {
542 zend_llist_destroy(auto_list);
543 efree(auto_list);
544 }
545
546 #include "php_psi.h"
547
548 void psi_call_frame_free(struct psi_call_frame *frame) {
549 zend_hash_destroy(&frame->arguments);
550 zend_hash_destroy(&frame->symbols);
551 if (frame->impl && frame->impl->func->static_memory) {
552 zend_llist *temp = emalloc(sizeof(*temp));
553 zval zlocal;
554
555 memcpy(temp, &frame->temp, sizeof(*temp));
556 ZVAL_OBJ(&zlocal, psi_object_init_ex(NULL, temp, psi_call_frame_local_auto_dtor));
557 zend_set_local_var_str(frame->impl->func->name,
558 strlen(frame->impl->func->name), &zlocal, /* force */ 1);
559 } else {
560 zend_llist_destroy(&frame->temp);
561 }
562 efree(frame->pointers);
563 efree(frame);
564 }
565
566
567