Skip to content

Commit 94ccab6

Browse files
committed
ext/pcntl: Refactor usage of strlcpy
We allocate the buffer, so we know that it will fit. Drive by refactorings and questions
1 parent c8bbb2b commit 94ccab6

File tree

1 file changed

+40
-38
lines changed

1 file changed

+40
-38
lines changed

ext/pcntl/pcntl.c

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -620,86 +620,88 @@ PHP_FUNCTION(pcntl_wstopsig)
620620
/* {{{ Executes specified program in current process space as defined by exec(2) */
621621
PHP_FUNCTION(pcntl_exec)
622622
{
623-
zval *args = NULL, *envs = NULL;
623+
zval *args = NULL;
624+
HashTable *env_vars_ht = NULL;
624625
zval *element;
625-
HashTable *args_hash, *envs_hash;
626-
int argc = 0, argi = 0;
627-
int envc = 0, envi = 0;
628626
char **argv = NULL, **envp = NULL;
629-
char **current_arg, **pair;
630-
size_t pair_length;
631-
zend_string *key;
632627
char *path;
633628
size_t path_len;
634-
zend_ulong key_num;
635629

636630
ZEND_PARSE_PARAMETERS_START(1, 3)
637631
Z_PARAM_PATH(path, path_len)
638632
Z_PARAM_OPTIONAL
639633
Z_PARAM_ARRAY(args)
640-
Z_PARAM_ARRAY(envs)
634+
Z_PARAM_ARRAY_HT(env_vars_ht)
641635
ZEND_PARSE_PARAMETERS_END();
642636

643-
if (ZEND_NUM_ARGS() > 1) {
637+
if (args != NULL) {
638+
// TODO Check array is a list?
644639
/* Build argument list */
645640
SEPARATE_ARRAY(args);
646-
args_hash = Z_ARRVAL_P(args);
647-
argc = zend_hash_num_elements(args_hash);
641+
const HashTable *args_ht = Z_ARRVAL_P(args);
642+
uint32_t argc = zend_hash_num_elements(args_ht);
648643

644+
/* We want a NULL terminated array of char* with the first entry being the path,
645+
* followed by the arguments */
649646
argv = safe_emalloc((argc + 2), sizeof(char *), 0);
650-
*argv = path;
651-
current_arg = argv+1;
652-
ZEND_HASH_FOREACH_VAL(args_hash, element) {
653-
if (argi >= argc) break;
647+
argv[0] = path;
648+
char **current_arg = argv+1;
649+
ZEND_HASH_FOREACH_VAL(args_ht, element) {
654650
if (!try_convert_to_string(element)) {
655651
efree(argv);
656652
RETURN_THROWS();
657653
}
654+
// TODO Check element does not have nul bytes?
658655

659656
*current_arg = Z_STRVAL_P(element);
660-
argi++;
661657
current_arg++;
662658
} ZEND_HASH_FOREACH_END();
663659
*current_arg = NULL;
664660
} else {
665-
argv = emalloc(2 * sizeof(char *));
661+
argv = safe_emalloc(2, sizeof(char *), 0);
666662
argv[0] = path;
667663
argv[1] = NULL;
668664
}
669665

670-
if ( ZEND_NUM_ARGS() == 3 ) {
666+
if (env_vars_ht != NULL) {
671667
/* Build environment pair list */
672-
SEPARATE_ARRAY(envs);
673-
envs_hash = Z_ARRVAL_P(envs);
674-
envc = zend_hash_num_elements(envs_hash);
668+
char **pair;
669+
zend_ulong key_num;
670+
zend_string *key;
675671

676-
size_t envp_len = (envc + 1);
672+
/* We want a NULL terminated array of char* */
673+
size_t envp_len = zend_hash_num_elements(env_vars_ht) + 1;
677674
pair = envp = safe_emalloc(envp_len, sizeof(char *), 0);
678675
memset(envp, 0, sizeof(char *) * envp_len);
679-
ZEND_HASH_FOREACH_KEY_VAL(envs_hash, key_num, key, element) {
680-
if (envi >= envc) break;
676+
ZEND_HASH_FOREACH_KEY_VAL(env_vars_ht, key_num, key, element) {
677+
zend_string *element_str = zval_try_get_string(element);
678+
if (element_str == NULL) {
679+
goto cleanup_env_vars;
680+
}
681+
681682
if (!key) {
682-
key = zend_long_to_str(key_num);
683+
// TODO Does it even make sense to have an environment variable which is an integer?
684+
key = zend_long_to_str((zend_long) key_num);
683685
} else {
684686
zend_string_addref(key);
685687
}
686688

687-
if (!try_convert_to_string(element)) {
688-
zend_string_release(key);
689-
goto cleanup_env_vars;
690-
}
689+
// TODO Check key and element do not have nul bytes?
691690

692691
/* Length of element + equal sign + length of key + null */
693-
ZEND_ASSERT(Z_STRLEN_P(element) < SIZE_MAX && ZSTR_LEN(key) < SIZE_MAX);
694-
*pair = safe_emalloc(Z_STRLEN_P(element) + 1, sizeof(char), ZSTR_LEN(key) + 1);
695-
pair_length = Z_STRLEN_P(element) + ZSTR_LEN(key) + 2;
696-
strlcpy(*pair, ZSTR_VAL(key), ZSTR_LEN(key) + 1);
697-
strlcat(*pair, "=", pair_length);
698-
strlcat(*pair, Z_STRVAL_P(element), pair_length);
692+
const uint8_t equal_len = strlen("=");
693+
size_t pair_length = ZSTR_LEN(element_str) + equal_len + ZSTR_LEN(key) + 1;
694+
ZEND_ASSERT(pair_length < SIZE_MAX);
695+
*pair = emalloc(pair_length);
696+
/* Copy key=element + final null byte into buffer */
697+
memcpy(*pair, ZSTR_VAL(key), ZSTR_LEN(key));
698+
memcpy(*pair + ZSTR_LEN(key), "=", equal_len);
699+
/* Copy null byte */
700+
memcpy(*pair + ZSTR_LEN(key) + equal_len, ZSTR_VAL(element_str), ZSTR_LEN(element_str) + 1);
699701

700702
/* Cleanup */
701-
zend_string_release_ex(key, 0);
702-
envi++;
703+
zend_string_release_ex(key, false);
704+
zend_string_release_ex(element_str, false);
703705
pair++;
704706
} ZEND_HASH_FOREACH_END();
705707
*(pair) = NULL;

0 commit comments

Comments
 (0)