diff options
| -rw-r--r-- | config/default.rules | 4 | ||||
| -rw-r--r-- | doc/source/config.rst | 2 | ||||
| -rw-r--r-- | doc/source/features.rst | 6 | ||||
| -rw-r--r-- | src/snuffleupagus.c | 52 | ||||
| -rw-r--r-- | src/sp_config_utils.c | 5 | ||||
| -rw-r--r-- | src/sp_cookie_encryption.c | 5 | ||||
| -rw-r--r-- | src/sp_crypt.c | 9 | ||||
| -rw-r--r-- | src/sp_ifilter.c | 10 | ||||
| -rw-r--r-- | src/sp_upload_validation.c | 8 | ||||
| -rw-r--r-- | src/sp_utils.c | 23 | ||||
| -rw-r--r-- | src/sp_wrapper.c | 2 | ||||
| -rw-r--r-- | src/tests/disable_function/disabled_function_echo_2.phpt | 6 | ||||
| -rw-r--r-- | src/tests/unserialize_php8/equivalent.phpt | 23 |
13 files changed, 125 insertions, 30 deletions
diff --git a/config/default.rules b/config/default.rules index 3e82ae3..0fa4878 100644 --- a/config/default.rules +++ b/config/default.rules | |||
| @@ -35,6 +35,10 @@ sp.xxe_protection.enable(); | |||
| 35 | # https://snuffleupagus.readthedocs.io/features.html#protection-against-cross-site-request-forgery | 35 | # https://snuffleupagus.readthedocs.io/features.html#protection-against-cross-site-request-forgery |
| 36 | sp.cookie.name("PHPSESSID").samesite("lax"); | 36 | sp.cookie.name("PHPSESSID").samesite("lax"); |
| 37 | 37 | ||
| 38 | # Note that an attacker with arbitrary PHP code execution | ||
| 39 | # can bypass some virtual-patching, by (as)using PHP feature. | ||
| 40 | # A clever example would be to declare a class with a __toString method. | ||
| 41 | |||
| 38 | # Harden the `chmod` function (0777 (oct = 511, 0666 = 438) | 42 | # Harden the `chmod` function (0777 (oct = 511, 0666 = 438) |
| 39 | @condition PHP_VERSION_ID < 80000; | 43 | @condition PHP_VERSION_ID < 80000; |
| 40 | sp.disable_function.function("chmod").param("mode").value("438").drop(); | 44 | sp.disable_function.function("chmod").param("mode").value("438").drop(); |
diff --git a/doc/source/config.rst b/doc/source/config.rst index 2053c2f..a84bb60 100644 --- a/doc/source/config.rst +++ b/doc/source/config.rst | |||
| @@ -152,7 +152,7 @@ least astonishment | |||
| 152 | <https://en.wikipedia.org/wiki/Principle_of_least_astonishment>`__. But since | 152 | <https://en.wikipedia.org/wiki/Principle_of_least_astonishment>`__. But since |
| 153 | it's `possible to modify php's logging system via php | 153 | it's `possible to modify php's logging system via php |
| 154 | <https://www.php.net/manual/en/errorfunc.configuration.php>`__, it's | 154 | <https://www.php.net/manual/en/errorfunc.configuration.php>`__, it's |
| 155 | heavily recommended to use the ``syslog`` option instead. The ``file:` option | 155 | heavily recommended to use the ``syslog`` option instead. The ``file:`` option |
| 156 | might be useful if you're using Snuffleupagus to fuzz or audit a codebase. | 156 | might be useful if you're using Snuffleupagus to fuzz or audit a codebase. |
| 157 | 157 | ||
| 158 | log_max_len | 158 | log_max_len |
diff --git a/doc/source/features.rst b/doc/source/features.rst index adb8779..517bbec 100644 --- a/doc/source/features.rst +++ b/doc/source/features.rst | |||
| @@ -309,7 +309,11 @@ of dangerous functions, dropping them everywhere else: | |||
| 309 | :language: php | 309 | :language: php |
| 310 | 310 | ||
| 311 | 311 | ||
| 312 | The intent is to make post-exploitation process (such as backdooring of legitimate code, or RAT usage) a lot harder for the attacker. | 312 | The intent is to make post-exploitation process (such as backdooring of |
| 313 | legitimate code, or RAT usage) a lot harder for the attacker. | ||
| 314 | |||
| 315 | Note that an attacker able to run arbitrary PHP code can likely bypass some virtual-patching | ||
| 316 | by (ab)using some PHP features. | ||
| 313 | 317 | ||
| 314 | 318 | ||
| 315 | .. _global-strict-feature: | 319 | .. _global-strict-feature: |
diff --git a/src/snuffleupagus.c b/src/snuffleupagus.c index 4d5fa09..6b0a327 100644 --- a/src/snuffleupagus.c +++ b/src/snuffleupagus.c | |||
| @@ -25,6 +25,39 @@ static inline void sp_op_array_handler(zend_op_array *const op) { | |||
| 25 | op->fn_flags |= ZEND_ACC_STRICT_TYPES; | 25 | op->fn_flags |= ZEND_ACC_STRICT_TYPES; |
| 26 | } | 26 | } |
| 27 | } | 27 | } |
| 28 | #if PHP_VERSION_ID >= 80500 | ||
| 29 | /* Prevent opcache from inlining user functions that have return-value | ||
| 30 | * monitoring rules, otherwise zend_execute_ex is never called and the | ||
| 31 | * hook never fires. ZEND_ACC_HAS_TYPE_HINTS is checked by | ||
| 32 | * zend_try_inline_call() and blocks inlining. For functions without | ||
| 33 | * actual type hints the only runtime effect is that ZEND_RECV opcodes | ||
| 34 | * are executed instead of skipped; for 0-arg functions (the common | ||
| 35 | * inlineable case) there are no RECV opcodes so the impact is zero. */ | ||
| 36 | if (op->function_name && ZEND_USER_CODE(op->type) && | ||
| 37 | ((SPCFG(disabled_functions_ret) && zend_hash_num_elements(SPCFG(disabled_functions_ret))) || | ||
| 38 | SPCFG(disabled_functions_reg_ret).disabled_functions)) { | ||
| 39 | char *fname = NULL; | ||
| 40 | if (op->scope) { | ||
| 41 | const size_t len = ZSTR_LEN(op->scope->name) + 2 + ZSTR_LEN(op->function_name) + 1; | ||
| 42 | fname = emalloc(len); | ||
| 43 | snprintf(fname, len, "%s::%s", ZSTR_VAL(op->scope->name), ZSTR_VAL(op->function_name)); | ||
| 44 | } else { | ||
| 45 | fname = estrdup(ZSTR_VAL(op->function_name)); | ||
| 46 | } | ||
| 47 | bool has_ret_rule = false; | ||
| 48 | if (SPCFG(disabled_functions_ret) && | ||
| 49 | zend_hash_str_find_ptr(SPCFG(disabled_functions_ret), fname, strlen(fname))) { | ||
| 50 | has_ret_rule = true; | ||
| 51 | } | ||
| 52 | if (!has_ret_rule && SPCFG(disabled_functions_reg_ret).disabled_functions) { | ||
| 53 | has_ret_rule = true; /* regex rules require runtime matching */ | ||
| 54 | } | ||
| 55 | if (has_ret_rule) { | ||
| 56 | op->fn_flags |= ZEND_ACC_HAS_TYPE_HINTS; | ||
| 57 | } | ||
| 58 | efree(fname); | ||
| 59 | } | ||
| 60 | #endif | ||
| 28 | } | 61 | } |
| 29 | 62 | ||
| 30 | ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) | 63 | ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus) |
| @@ -246,7 +279,7 @@ PHP_MINFO_FUNCTION(snuffleupagus) { | |||
| 246 | php_info_print_table_start(); | 279 | php_info_print_table_start(); |
| 247 | php_info_print_table_row( | 280 | php_info_print_table_row( |
| 248 | 2, "snuffleupagus support", | 281 | 2, "snuffleupagus support", |
| 249 | SPG(is_config_valid) ? "enabled" : "disabled"); | 282 | SPG(is_config_valid) == SP_CONFIG_VALID ? "enabled" : "disabled"); |
| 250 | php_info_print_table_row(2, "Version", PHP_SNUFFLEUPAGUS_VERSION); | 283 | php_info_print_table_row(2, "Version", PHP_SNUFFLEUPAGUS_VERSION); |
| 251 | php_info_print_table_row(2, "Valid config", valid_config); | 284 | php_info_print_table_row(2, "Valid config", valid_config); |
| 252 | php_info_print_table_end(); | 285 | php_info_print_table_end(); |
| @@ -582,12 +615,25 @@ static PHP_INI_MH(OnUpdateConfiguration) { | |||
| 582 | 615 | ||
| 583 | sp_hook_register_server_variables(); | 616 | sp_hook_register_server_variables(); |
| 584 | 617 | ||
| 585 | if (SPCFG(global_strict).enable) { | 618 | bool need_op_array_handler = SPCFG(global_strict).enable; |
| 619 | |||
| 620 | #if PHP_VERSION_ID >= 80500 | ||
| 621 | /* Register as zend extension to get op_array_handler callbacks, which we | ||
| 622 | * use to prevent opcache from inlining monitored functions. */ | ||
| 623 | if (SPCFG(disabled_functions_ret) && zend_hash_num_elements(SPCFG(disabled_functions_ret))) { | ||
| 624 | need_op_array_handler = true; | ||
| 625 | } | ||
| 626 | if (SPCFG(disabled_functions_reg_ret).disabled_functions) { | ||
| 627 | need_op_array_handler = true; | ||
| 628 | } | ||
| 629 | #endif | ||
| 630 | |||
| 631 | if (need_op_array_handler) { | ||
| 586 | if (!zend_get_extension(PHP_SNUFFLEUPAGUS_EXTNAME)) { | 632 | if (!zend_get_extension(PHP_SNUFFLEUPAGUS_EXTNAME)) { |
| 587 | zend_extension_entry.startup = NULL; | 633 | zend_extension_entry.startup = NULL; |
| 588 | zend_register_extension(&zend_extension_entry, NULL); | 634 | zend_register_extension(&zend_extension_entry, NULL); |
| 589 | } | 635 | } |
| 590 | // This is needed to implement the global strict mode | 636 | // This is needed to enable the op_array_handler callback |
| 591 | CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY; | 637 | CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY; |
| 592 | } | 638 | } |
| 593 | 639 | ||
diff --git a/src/sp_config_utils.c b/src/sp_config_utils.c index 84b1f30..415e434 100644 --- a/src/sp_config_utils.c +++ b/src/sp_config_utils.c | |||
| @@ -11,8 +11,9 @@ sp_list_node *parse_functions_list(const char *const value) { | |||
| 11 | sp_list_node *list = NULL; | 11 | sp_list_node *list = NULL; |
| 12 | char *tmp = strdup(value); | 12 | char *tmp = strdup(value); |
| 13 | const char *function_name; | 13 | const char *function_name; |
| 14 | char *next_token = tmp; | 14 | char *next_token = NULL; |
| 15 | while ((function_name = strtok_r(NULL, sep, &next_token))) { | 15 | for (function_name = strtok_r(tmp, sep, &next_token); function_name; |
| 16 | function_name = strtok_r(NULL, sep, &next_token)) { | ||
| 16 | list = sp_list_prepend(list, strdup(function_name)); | 17 | list = sp_list_prepend(list, strdup(function_name)); |
| 17 | } | 18 | } |
| 18 | free(tmp); | 19 | free(tmp); |
diff --git a/src/sp_cookie_encryption.c b/src/sp_cookie_encryption.c index 0c14c70..7b3e088 100644 --- a/src/sp_cookie_encryption.c +++ b/src/sp_cookie_encryption.c | |||
| @@ -31,6 +31,11 @@ int decrypt_cookie(zval *pDest, int num_args, va_list args, | |||
| 31 | return ZEND_HASH_APPLY_KEEP; | 31 | return ZEND_HASH_APPLY_KEEP; |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | /* Cookies can be arrays, like session_id[]=x */ | ||
| 35 | if (Z_TYPE_P(pDest) != IS_STRING) { | ||
| 36 | return ZEND_HASH_APPLY_KEEP; | ||
| 37 | } | ||
| 38 | |||
| 34 | /* If the cookie has no value, it shouldn't be encrypted. */ | 39 | /* If the cookie has no value, it shouldn't be encrypted. */ |
| 35 | if (0 == Z_STRLEN_P(pDest)) { | 40 | if (0 == Z_STRLEN_P(pDest)) { |
| 36 | return ZEND_HASH_APPLY_KEEP; | 41 | return ZEND_HASH_APPLY_KEEP; |
diff --git a/src/sp_crypt.c b/src/sp_crypt.c index 6d48554..3b65616 100644 --- a/src/sp_crypt.c +++ b/src/sp_crypt.c | |||
| @@ -32,6 +32,7 @@ void generate_key(unsigned char *key) { | |||
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | PHP_SHA256Final((unsigned char *)key, &ctx); | 34 | PHP_SHA256Final((unsigned char *)key, &ctx); |
| 35 | ZEND_SECURE_ZERO(&ctx, sizeof(ctx)); | ||
| 35 | } | 36 | } |
| 36 | 37 | ||
| 37 | // This function return 0 upon success , non-zero otherwise | 38 | // This function return 0 upon success , non-zero otherwise |
| @@ -42,6 +43,11 @@ int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hash_key) { | |||
| 42 | 43 | ||
| 43 | zend_string *debase64 = php_base64_decode((unsigned char *)(Z_STRVAL_P(pDest)), Z_STRLEN_P(pDest)); | 44 | zend_string *debase64 = php_base64_decode((unsigned char *)(Z_STRVAL_P(pDest)), Z_STRLEN_P(pDest)); |
| 44 | 45 | ||
| 46 | if (!debase64) { | ||
| 47 | sp_log_drop( "cookie_encryption", "Unable to base64-decode the cookie"); | ||
| 48 | return ZEND_HASH_APPLY_REMOVE; | ||
| 49 | } | ||
| 50 | |||
| 45 | if (ZSTR_LEN(debase64) < crypto_secretbox_NONCEBYTES) { | 51 | if (ZSTR_LEN(debase64) < crypto_secretbox_NONCEBYTES) { |
| 46 | if (true == simulation) { | 52 | if (true == simulation) { |
| 47 | sp_log_simulation( | 53 | sp_log_simulation( |
| @@ -115,6 +121,7 @@ int decrypt_zval(zval *pDest, bool simulation, zend_hash_key *hash_key) { | |||
| 115 | ret = ZEND_HASH_APPLY_KEEP; | 121 | ret = ZEND_HASH_APPLY_KEEP; |
| 116 | 122 | ||
| 117 | out: | 123 | out: |
| 124 | ZEND_SECURE_ZERO(key, sizeof(key)); | ||
| 118 | zend_string_efree(debase64); | 125 | zend_string_efree(debase64); |
| 119 | efree(decrypted); | 126 | efree(decrypted); |
| 120 | efree(backup); | 127 | efree(backup); |
| @@ -164,6 +171,8 @@ zend_string *encrypt_zval(zend_string *data) { | |||
| 164 | z = php_base64_encode(encrypted_data, emsg_and_nonce_len); | 171 | z = php_base64_encode(encrypted_data, emsg_and_nonce_len); |
| 165 | } | 172 | } |
| 166 | 173 | ||
| 174 | ZEND_SECURE_ZERO(key, sizeof(key)); | ||
| 175 | ZEND_SECURE_ZERO(nonce, sizeof(nonce)); | ||
| 167 | efree(data_to_encrypt); | 176 | efree(data_to_encrypt); |
| 168 | efree(encrypted_data); | 177 | efree(encrypted_data); |
| 169 | 178 | ||
diff --git a/src/sp_ifilter.c b/src/sp_ifilter.c index 67eb5f3..ffdeec1 100644 --- a/src/sp_ifilter.c +++ b/src/sp_ifilter.c | |||
| @@ -33,7 +33,7 @@ static void sp_server_strip(HashTable *svars, const char *key, size_t keylen) { | |||
| 33 | char *tmpend = tmp + ZSTR_LEN(tmp_zstr); | 33 | char *tmpend = tmp + ZSTR_LEN(tmp_zstr); |
| 34 | 34 | ||
| 35 | for (char *p = tmp; p < tmpend; p++) { | 35 | for (char *p = tmp; p < tmpend; p++) { |
| 36 | if (sp_is_dangerous_char[(int)*p]) { | 36 | if (sp_is_dangerous_char[(unsigned char)*p]) { |
| 37 | *p = '_'; | 37 | *p = '_'; |
| 38 | } | 38 | } |
| 39 | } | 39 | } |
| @@ -49,17 +49,17 @@ static void sp_server_encode(HashTable *svars, const char *key, size_t keylen) { | |||
| 49 | int extra = 0; | 49 | int extra = 0; |
| 50 | 50 | ||
| 51 | for (char *p = tmp; p < tmpend; p++) { | 51 | for (char *p = tmp; p < tmpend; p++) { |
| 52 | extra += sp_is_dangerous_char[(int)*p] * 2; | 52 | extra += sp_is_dangerous_char[(unsigned char)*p] * 2; |
| 53 | } | 53 | } |
| 54 | if (!extra) { return; } | 54 | if (!extra) { return; } |
| 55 | 55 | ||
| 56 | zend_string *new_zstr = zend_string_alloc(ZSTR_LEN(tmp_zstr) + extra, 0); | 56 | zend_string *new_zstr = zend_string_alloc(ZSTR_LEN(tmp_zstr) + extra, 0); |
| 57 | char *n = ZSTR_VAL(new_zstr); | 57 | char *n = ZSTR_VAL(new_zstr); |
| 58 | for (char *p = tmp; p < tmpend; p++, n++) { | 58 | for (char *p = tmp; p < tmpend; p++, n++) { |
| 59 | if (sp_is_dangerous_char[(int)*p]) { | 59 | if (sp_is_dangerous_char[(unsigned char)*p]) { |
| 60 | *n++ = '%'; | 60 | *n++ = '%'; |
| 61 | *n++ = sp_hexchars[*p >> 4]; | 61 | *n++ = sp_hexchars[(unsigned char)*p >> 4]; |
| 62 | *n = sp_hexchars[*p & 15]; | 62 | *n = sp_hexchars[(unsigned char)*p & 15]; |
| 63 | } else { | 63 | } else { |
| 64 | *n = *p; | 64 | *n = *p; |
| 65 | } | 65 | } |
diff --git a/src/sp_upload_validation.c b/src/sp_upload_validation.c index 4ac4992..b5babed 100644 --- a/src/sp_upload_validation.c +++ b/src/sp_upload_validation.c | |||
| @@ -54,8 +54,9 @@ static int sp_rfc1867_callback(unsigned int event, void *event_data, void **extr | |||
| 54 | cmd[1] = tmp_name; | 54 | cmd[1] = tmp_name; |
| 55 | cmd[2] = NULL; | 55 | cmd[2] = NULL; |
| 56 | 56 | ||
| 57 | const char *remote_addr = getenv("REMOTE_ADDR"); | ||
| 57 | spprintf(&env[0], 0, "SP_FILENAME=%s", filename); | 58 | spprintf(&env[0], 0, "SP_FILENAME=%s", filename); |
| 58 | spprintf(&env[1], 0, "SP_REMOTE_ADDR=%s", getenv("REMOTE_ADDR")); | 59 | spprintf(&env[1], 0, "SP_REMOTE_ADDR=%s", remote_addr ? remote_addr : ""); |
| 59 | spprintf(&env[2], 0, "SP_CURRENT_FILE=%s", zend_get_executed_filename(TSRMLS_C)); | 60 | spprintf(&env[2], 0, "SP_CURRENT_FILE=%s", zend_get_executed_filename(TSRMLS_C)); |
| 60 | spprintf(&env[3], 0, "SP_FILESIZE=%zu", filesize); | 61 | spprintf(&env[3], 0, "SP_FILESIZE=%zu", filesize); |
| 61 | env[4] = NULL; | 62 | env[4] = NULL; |
| @@ -63,8 +64,7 @@ static int sp_rfc1867_callback(unsigned int event, void *event_data, void **extr | |||
| 63 | if ((pid = fork()) == 0) { | 64 | if ((pid = fork()) == 0) { |
| 64 | if (execve(ZSTR_VAL(config_upload->script), cmd, env) == -1) { | 65 | if (execve(ZSTR_VAL(config_upload->script), cmd, env) == -1) { |
| 65 | sp_log_warn("upload_validation", "Could not call '%s' : %s", ZSTR_VAL(config_upload->script), strerror(errno)); | 66 | sp_log_warn("upload_validation", "Could not call '%s' : %s", ZSTR_VAL(config_upload->script), strerror(errno)); |
| 66 | EFREE_3(env); | 67 | _exit(1); |
| 67 | exit(1); | ||
| 68 | } | 68 | } |
| 69 | } else if (pid == -1) { | 69 | } else if (pid == -1) { |
| 70 | // LCOV_EXCL_START | 70 | // LCOV_EXCL_START |
| @@ -76,7 +76,7 @@ static int sp_rfc1867_callback(unsigned int event, void *event_data, void **extr | |||
| 76 | 76 | ||
| 77 | EFREE_3(env); | 77 | EFREE_3(env); |
| 78 | int waitstatus; | 78 | int waitstatus; |
| 79 | wait(&waitstatus); | 79 | waitpid(pid, &waitstatus, 0); |
| 80 | if (WEXITSTATUS(waitstatus) != 0) { // Nope | 80 | if (WEXITSTATUS(waitstatus) != 0) { // Nope |
| 81 | char *uri = getenv("REQUEST_URI"); | 81 | char *uri = getenv("REQUEST_URI"); |
| 82 | int sim = config_upload->simulation; | 82 | int sim = config_upload->simulation; |
diff --git a/src/sp_utils.c b/src/sp_utils.c index d49d459..10beb8b 100644 --- a/src/sp_utils.c +++ b/src/sp_utils.c | |||
| @@ -315,11 +315,11 @@ void sp_log_disable(const char* restrict path, const char* restrict arg_name, | |||
| 315 | if (arg_name) { | 315 | if (arg_name) { |
| 316 | char* char_repr = NULL; | 316 | char* char_repr = NULL; |
| 317 | if (arg_value) { | 317 | if (arg_value) { |
| 318 | zend_string *arg_value_dup = zend_string_init(ZSTR_VAL(arg_value), ZSTR_LEN(arg_value), 0); | 318 | zend_string *arg_value_enc = php_raw_url_encode(ZSTR_VAL(arg_value), ZSTR_LEN(arg_value)); |
| 319 | arg_value_dup = php_raw_url_encode(ZSTR_VAL(arg_value_dup), ZSTR_LEN(arg_value_dup)); | 319 | char_repr = zend_string_to_char(arg_value_enc); |
| 320 | char_repr = zend_string_to_char(arg_value_dup); | 320 | size_t max_len = MIN(ZSTR_LEN(arg_value_enc), (size_t)SPCFG(log_max_len)); |
| 321 | size_t max_len = MIN(ZSTR_LEN(arg_value_dup), (size_t)SPCFG(log_max_len)); | ||
| 322 | char_repr[max_len] = '\0'; | 321 | char_repr[max_len] = '\0'; |
| 322 | zend_string_release(arg_value_enc); | ||
| 323 | } | 323 | } |
| 324 | if (alias) { | 324 | if (alias) { |
| 325 | sp_log_auto( | 325 | sp_log_auto( |
| @@ -359,11 +359,11 @@ void sp_log_disable_ret(const char* restrict path, | |||
| 359 | sp_log_request(dump, config_node->textual_representation); | 359 | sp_log_request(dump, config_node->textual_representation); |
| 360 | } | 360 | } |
| 361 | if (ret_value) { | 361 | if (ret_value) { |
| 362 | zend_string *ret_value_dup = zend_string_init(ZSTR_VAL(ret_value), ZSTR_LEN(ret_value), 0); | 362 | zend_string *ret_value_enc = php_raw_url_encode(ZSTR_VAL(ret_value), ZSTR_LEN(ret_value)); |
| 363 | ret_value_dup = php_raw_url_encode(ZSTR_VAL(ret_value_dup), ZSTR_LEN(ret_value_dup)); | 363 | char_repr = zend_string_to_char(ret_value_enc); |
| 364 | char_repr = zend_string_to_char(ret_value_dup); | 364 | size_t max_len = MIN(ZSTR_LEN(ret_value_enc), (size_t)SPCFG(log_max_len)); |
| 365 | size_t max_len = MIN(ZSTR_LEN(ret_value_dup), (size_t)SPCFG(log_max_len)); | ||
| 366 | char_repr[max_len] = '\0'; | 365 | char_repr[max_len] = '\0'; |
| 366 | zend_string_release(ret_value_enc); | ||
| 367 | } | 367 | } |
| 368 | if (alias) { | 368 | if (alias) { |
| 369 | sp_log_auto( | 369 | sp_log_auto( |
| @@ -393,11 +393,12 @@ bool sp_match_array_key(const zval* zv, const zend_string* to_match, const sp_re | |||
| 393 | char* idx_str = NULL; | 393 | char* idx_str = NULL; |
| 394 | spprintf(&idx_str, 0, ZEND_ULONG_FMT, idx); | 394 | spprintf(&idx_str, 0, ZEND_ULONG_FMT, idx); |
| 395 | zend_string* tmp = zend_string_init(idx_str, strlen(idx_str), 0); | 395 | zend_string* tmp = zend_string_init(idx_str, strlen(idx_str), 0); |
| 396 | if (sp_match_value(tmp, to_match, rx)) { | 396 | bool match = sp_match_value(tmp, to_match, rx); |
| 397 | efree(idx_str); | 397 | zend_string_release(tmp); |
| 398 | efree(idx_str); | ||
| 399 | if (match) { | ||
| 398 | return true; | 400 | return true; |
| 399 | } | 401 | } |
| 400 | efree(idx_str); | ||
| 401 | } | 402 | } |
| 402 | } | 403 | } |
| 403 | ZEND_HASH_FOREACH_END(); | 404 | ZEND_HASH_FOREACH_END(); |
diff --git a/src/sp_wrapper.c b/src/sp_wrapper.c index 6b6c5cd..22b1d88 100644 --- a/src/sp_wrapper.c +++ b/src/sp_wrapper.c | |||
| @@ -180,7 +180,7 @@ PHP_FUNCTION(sp_stream_wrapper_register) { | |||
| 180 | if (!protocol_name || wrapper_is_whitelisted(protocol_name)) { | 180 | if (!protocol_name || wrapper_is_whitelisted(protocol_name)) { |
| 181 | 181 | ||
| 182 | // reject manual loading of "php" wrapper | 182 | // reject manual loading of "php" wrapper |
| 183 | if (!strcasecmp(ZSTR_VAL(protocol_name), "php") && sp_php_stream_is_filtered()) { | 183 | if (protocol_name && !strcasecmp(ZSTR_VAL(protocol_name), "php") && sp_php_stream_is_filtered()) { |
| 184 | return; | 184 | return; |
| 185 | } | 185 | } |
| 186 | 186 | ||
diff --git a/src/tests/disable_function/disabled_function_echo_2.phpt b/src/tests/disable_function/disabled_function_echo_2.phpt index c1d9817..ce3488e 100644 --- a/src/tests/disable_function/disabled_function_echo_2.phpt +++ b/src/tests/disable_function/disabled_function_echo_2.phpt | |||
| @@ -4,11 +4,13 @@ Echo hooking | |||
| 4 | <?php if (!extension_loaded("snuffleupagus")) print "skip"; ?> | 4 | <?php if (!extension_loaded("snuffleupagus")) print "skip"; ?> |
| 5 | --INI-- | 5 | --INI-- |
| 6 | sp.configuration_file={PWD}/config/disabled_function_echo.ini | 6 | sp.configuration_file={PWD}/config/disabled_function_echo.ini |
| 7 | opcache.optimization_level=0 | ||
| 7 | --FILE-- | 8 | --FILE-- |
| 8 | <?php | 9 | <?php |
| 9 | echo "qwe"; | 10 | echo "qwe"; |
| 10 | echo "1", "oops"; | 11 | echo "1"; |
| 12 | echo "oops"; | ||
| 11 | ?> | 13 | ?> |
| 12 | --EXPECTF-- | 14 | --EXPECTF-- |
| 13 | qwe1 | 15 | qwe1 |
| 14 | Fatal error: [snuffleupagus][0.0.0.0][disabled_function][drop] Aborted execution on call of the function 'echo' in %a/disabled_function_echo_2.php on line 3 | 16 | Fatal error: [snuffleupagus][0.0.0.0][disabled_function][drop] Aborted execution on call of the function 'echo' in %a/disabled_function_echo_2.php on line 4 |
diff --git a/src/tests/unserialize_php8/equivalent.phpt b/src/tests/unserialize_php8/equivalent.phpt new file mode 100644 index 0000000..57dcf72 --- /dev/null +++ b/src/tests/unserialize_php8/equivalent.phpt | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | --TEST-- | ||
| 2 | Test idempotence of serialize+unserialize | ||
| 3 | --SKIPIF-- | ||
| 4 | <?php if (!extension_loaded("snuffleupagus")) print "skip"; ?> | ||
| 5 | --INI-- | ||
| 6 | sp.configuration_file={PWD}/config/config_serialize.ini | ||
| 7 | --FILE-- | ||
| 8 | <?php | ||
| 9 | class A { | ||
| 10 | public $pub = "public data"; | ||
| 11 | protected $prot = "protected data"; | ||
| 12 | private $priv = "private data"; | ||
| 13 | } | ||
| 14 | $a = new A; | ||
| 15 | if (unserialize(serialize($a)) == $a) { | ||
| 16 | echo "OK"; | ||
| 17 | } else { | ||
| 18 | echo "FAIL"; | ||
| 19 | } | ||
| 20 | ?> | ||
| 21 | --EXPECT-- | ||
| 22 | OK | ||
| 23 | |||
