34
34
#include "php_globals.h"
35
35
#include "SAPI.h"
36
36
#include "main/php_network.h"
37
+ #include "zend_smart_string.h"
37
38
38
39
#if HAVE_SYS_WAIT_H
39
40
#include <sys/wait.h>
@@ -399,12 +400,87 @@ struct php_proc_open_descriptor_item {
399
400
};
400
401
/* }}} */
401
402
403
+ static zend_string * get_valid_arg_string (zval * zv , int elem_num ) {
404
+ zend_string * str = zval_get_string (zv );
405
+ if (!str ) {
406
+ return NULL ;
407
+ }
408
+
409
+ if (strlen (ZSTR_VAL (str )) != ZSTR_LEN (str )) {
410
+ php_error_docref (NULL , E_WARNING ,
411
+ "Command array element %d contains a null byte" , elem_num );
412
+ zend_string_release (str );
413
+ return NULL ;
414
+ }
415
+
416
+ return str ;
417
+ }
418
+
419
+ #ifdef PHP_WIN32
420
+ static void append_backslashes (smart_string * str , size_t num_bs ) {
421
+ size_t i ;
422
+ for (i = 0 ; i < num_bs ; i ++ ) {
423
+ smart_string_appendc (str , '\\' );
424
+ }
425
+ }
426
+
427
+ /* See https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments */
428
+ static void append_win_escaped_arg (smart_string * str , char * arg ) {
429
+ char c ;
430
+ size_t num_bs = 0 ;
431
+ smart_string_appendc (str , '"' );
432
+ while ((c = * arg )) {
433
+ if (c == '\\' ) {
434
+ num_bs ++ ;
435
+ } else {
436
+ if (c == '"' ) {
437
+ /* Backslashes before " need to be doubled. */
438
+ num_bs = num_bs * 2 + 1 ;
439
+ }
440
+ append_backslashes (str , num_bs );
441
+ smart_string_appendc (str , c );
442
+ num_bs = 0 ;
443
+ }
444
+ arg ++ ;
445
+ }
446
+ append_backslashes (str , num_bs * 2 );
447
+ smart_string_appendc (str , '"' );
448
+ }
449
+
450
+ static char * create_win_command_from_args (HashTable * args ) {
451
+ smart_string str = {0 };
452
+ zval * arg_zv ;
453
+ zend_bool is_prog_name = 1 ;
454
+ int elem_num = 0 ;
455
+
456
+ ZEND_HASH_FOREACH_VAL (args , arg_zv ) {
457
+ zend_string * arg_str = get_valid_arg_string (arg_zv , ++ elem_num );
458
+ if (!arg_str ) {
459
+ smart_string_free (& str );
460
+ return NULL ;
461
+ }
462
+
463
+ if (!is_prog_name ) {
464
+ smart_string_appendc (& str , ' ' );
465
+ }
466
+
467
+ append_win_escaped_arg (& str , ZSTR_VAL (arg_str ));
468
+
469
+ is_prog_name = 0 ;
470
+ zend_string_release (arg_str );
471
+ } ZEND_HASH_FOREACH_END ();
472
+ smart_string_0 (& str );
473
+ return str .c ;
474
+ }
475
+ #endif
476
+
402
477
/* {{{ proto resource proc_open(string command, array descriptorspec, array &pipes [, string cwd [, array env [, array other_options]]])
403
478
Run a process with more control over it's file descriptors */
404
479
PHP_FUNCTION (proc_open )
405
480
{
406
- char * command , * cwd = NULL ;
407
- size_t command_len , cwd_len = 0 ;
481
+ zval * command_zv ;
482
+ char * command = NULL , * cwd = NULL ;
483
+ size_t cwd_len = 0 ;
408
484
zval * descriptorspec ;
409
485
zval * pipes ;
410
486
zval * environment = NULL ;
@@ -428,23 +504,23 @@ PHP_FUNCTION(proc_open)
428
504
char cur_cwd [MAXPATHLEN ];
429
505
wchar_t * cmdw = NULL , * cwdw = NULL , * envpw = NULL ;
430
506
size_t tmp_len ;
431
- #endif
432
- php_process_id_t child ;
433
- struct php_process_handle * proc ;
434
- int is_persistent = 0 ; /* TODO: ensure that persistent procs will work */
435
- #ifdef PHP_WIN32
436
507
int suppress_errors = 0 ;
437
508
int bypass_shell = 0 ;
438
509
int blocking_pipes = 0 ;
439
510
int create_process_group = 0 ;
511
+ #else
512
+ char * * argv = NULL ;
440
513
#endif
514
+ php_process_id_t child ;
515
+ struct php_process_handle * proc ;
516
+ int is_persistent = 0 ; /* TODO: ensure that persistent procs will work */
441
517
#if PHP_CAN_DO_PTS
442
518
php_file_descriptor_t dev_ptmx = -1 ; /* master */
443
519
php_file_descriptor_t slave_pty = -1 ;
444
520
#endif
445
521
446
522
ZEND_PARSE_PARAMETERS_START (3 , 6 )
447
- Z_PARAM_STRING ( command , command_len )
523
+ Z_PARAM_ZVAL ( command_zv )
448
524
Z_PARAM_ARRAY (descriptorspec )
449
525
Z_PARAM_ZVAL (pipes )
450
526
Z_PARAM_OPTIONAL
@@ -453,7 +529,48 @@ PHP_FUNCTION(proc_open)
453
529
Z_PARAM_ARRAY_EX (other_options , 1 , 0 )
454
530
ZEND_PARSE_PARAMETERS_END_EX (RETURN_FALSE );
455
531
456
- command = pestrdup (command , is_persistent );
532
+ memset (& env , 0 , sizeof (env ));
533
+
534
+ if (Z_TYPE_P (command_zv ) == IS_ARRAY ) {
535
+ zval * arg_zv ;
536
+ uint32_t num_elems = zend_hash_num_elements (Z_ARRVAL_P (command_zv ));
537
+ if (num_elems == 0 ) {
538
+ php_error_docref (NULL , E_WARNING , "Command array must have at least one element" );
539
+ RETURN_FALSE ;
540
+ }
541
+
542
+ #ifdef PHP_WIN32
543
+ bypass_shell = 1 ;
544
+ command = create_win_command_from_args (Z_ARRVAL_P (command_zv ));
545
+ if (!command ) {
546
+ RETURN_FALSE ;
547
+ }
548
+ #else
549
+ argv = safe_emalloc (sizeof (char * ), num_elems + 1 , 0 );
550
+ i = 0 ;
551
+ ZEND_HASH_FOREACH_VAL (Z_ARRVAL_P (command_zv ), arg_zv ) {
552
+ zend_string * arg_str = get_valid_arg_string (arg_zv , i + 1 );
553
+ if (!arg_str ) {
554
+ argv [i ] = NULL ;
555
+ goto exit_fail ;
556
+ }
557
+
558
+ if (i == 0 ) {
559
+ command = pestrdup (ZSTR_VAL (arg_str ), is_persistent );
560
+ }
561
+
562
+ argv [i ++ ] = estrdup (ZSTR_VAL (arg_str ));
563
+ zend_string_release (arg_str );
564
+ } ZEND_HASH_FOREACH_END ();
565
+ argv [i ] = NULL ;
566
+
567
+ /* As the array is non-empty, we should have found a command. */
568
+ ZEND_ASSERT (command );
569
+ #endif
570
+ } else {
571
+ convert_to_string (command_zv );
572
+ command = pestrdup (Z_STRVAL_P (command_zv ), is_persistent );
573
+ }
457
574
458
575
#ifdef PHP_WIN32
459
576
if (other_options ) {
@@ -464,6 +581,7 @@ PHP_FUNCTION(proc_open)
464
581
}
465
582
}
466
583
584
+ /* TODO Deprecate in favor of array command? */
467
585
item = zend_hash_str_find (Z_ARRVAL_P (other_options ), "bypass_shell" , sizeof ("bypass_shell" ) - 1 );
468
586
if (item != NULL ) {
469
587
if (Z_TYPE_P (item ) == IS_TRUE || ((Z_TYPE_P (item ) == IS_LONG ) && Z_LVAL_P (item ))) {
@@ -487,12 +605,8 @@ PHP_FUNCTION(proc_open)
487
605
}
488
606
#endif
489
607
490
- command_len = strlen (command );
491
-
492
608
if (environment ) {
493
609
env = _php_array_to_envp (environment , is_persistent );
494
- } else {
495
- memset (& env , 0 , sizeof (env ));
496
610
}
497
611
498
612
ndescriptors_array = zend_hash_num_elements (Z_ARRVAL_P (descriptorspec ));
@@ -744,7 +858,7 @@ PHP_FUNCTION(proc_open)
744
858
}
745
859
}
746
860
747
- cmdw = php_win32_cp_conv_any_to_w (command , command_len , & tmp_len );
861
+ cmdw = php_win32_cp_conv_any_to_w (command , strlen ( command ) , & tmp_len );
748
862
if (!cmdw ) {
749
863
php_error_docref (NULL , E_WARNING , "Command conversion failed" );
750
864
goto exit_fail ;
@@ -852,10 +966,18 @@ PHP_FUNCTION(proc_open)
852
966
php_ignore_value (chdir (cwd ));
853
967
}
854
968
855
- if (env .envarray ) {
856
- execle ("/bin/sh" , "sh" , "-c" , command , NULL , env .envarray );
969
+ if (argv ) {
970
+ /* execvpe() is non-portable, use environ instead. */
971
+ if (env .envarray ) {
972
+ environ = env .envarray ;
973
+ }
974
+ execvp (command , argv );
857
975
} else {
858
- execl ("/bin/sh" , "sh" , "-c" , command , NULL );
976
+ if (env .envarray ) {
977
+ execle ("/bin/sh" , "sh" , "-c" , command , NULL , env .envarray );
978
+ } else {
979
+ execl ("/bin/sh" , "sh" , "-c" , command , NULL );
980
+ }
859
981
}
860
982
_exit (127 );
861
983
@@ -960,18 +1082,42 @@ PHP_FUNCTION(proc_open)
960
1082
}
961
1083
}
962
1084
1085
+ #ifndef PHP_WIN32
1086
+ if (argv ) {
1087
+ char * * arg = argv ;
1088
+ while (* arg != NULL ) {
1089
+ efree (* arg );
1090
+ arg ++ ;
1091
+ }
1092
+ efree (argv );
1093
+ }
1094
+ #endif
1095
+
963
1096
efree (descriptors );
964
1097
ZVAL_RES (return_value , zend_register_resource (proc , le_proc_open ));
965
1098
return ;
966
1099
967
1100
exit_fail :
968
- efree (descriptors );
1101
+ if (descriptors ) {
1102
+ efree (descriptors );
1103
+ }
969
1104
_php_free_envp (env , is_persistent );
970
- pefree (command , is_persistent );
1105
+ if (command ) {
1106
+ pefree (command , is_persistent );
1107
+ }
971
1108
#ifdef PHP_WIN32
972
1109
free (cwdw );
973
1110
free (cmdw );
974
1111
free (envpw );
1112
+ #else
1113
+ if (argv ) {
1114
+ char * * arg = argv ;
1115
+ while (* arg != NULL ) {
1116
+ efree (* arg );
1117
+ arg ++ ;
1118
+ }
1119
+ efree (argv );
1120
+ }
975
1121
#endif
976
1122
#if PHP_CAN_DO_PTS
977
1123
if (dev_ptmx >= 0 ) {
0 commit comments