diff --git a/NEWS b/NEWS index dd28760928e11..0e0a6652d4f58 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,10 @@ PHP NEWS - Core: . Fixed line number of JMP instruction over else block. (ilutov) + . Fixed use-of-uninitialized-value with ??= on assert. (ilutov) + +- OpenSSL + . Added support for additional EC parameters in openssl_pkey_new. (Eno-CN) 06 Jul 2023, PHP 8.3.0alpha3 diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 84ef7ac8bc7d4..cdc8932df15d3 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -67,6 +67,9 @@ PHP 8.3 INTERNALS UPGRADE NOTES - zend_set_user_opcode_handler - zend_ssa_inference * Removed unused macros PHP_FNV1_32A_INIT and PHP_FNV1A_64_INIT. See GH-11114. +* _php_stream_dirent now has an extra d_type field that is used to store the + directory entry type. This can be used to avoid additional stat calls for + types when the type is already known. ======================== 2. Build system changes @@ -115,6 +118,7 @@ PHP 8.3 INTERNALS UPGRADE NOTES e. ext/spl - The PHPAPI spl_iterator_apply() function now returns zend_result instead of int. There are no functional changes. + - The field _spl_filesystem_object->is_recursive has been removed. f. ext/dom - A new function dom_get_doc_props_read_only() is added to gather the document diff --git a/Zend/tests/gh11580.phpt b/Zend/tests/gh11580.phpt new file mode 100644 index 0000000000000..194c8b5a1aa0e --- /dev/null +++ b/Zend/tests/gh11580.phpt @@ -0,0 +1,13 @@ +--TEST-- +GH-11580: assert() with ??= operator can lead to use-of-uninitialized-value +--INI-- +zend.assertions=0 +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Error: Undefined constant "y" in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index ea71e075f8cce..85f19d32038ab 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -4203,6 +4203,10 @@ static void zend_compile_assert(znode *result, zend_ast_list *args, zend_string zend_op *opline; uint32_t check_op_number = get_next_op_number(); + /* Assert expression may not be memoized and reused as it may not actually be evaluated. */ + int orig_memoize_mode = CG(memoize_mode); + CG(memoize_mode) = ZEND_MEMOIZE_NONE; + zend_emit_op(NULL, ZEND_ASSERT_CHECK, NULL, NULL); if (fbc && fbc_is_finalized(fbc)) { @@ -4236,6 +4240,8 @@ static void zend_compile_assert(znode *result, zend_ast_list *args, zend_string opline = &CG(active_op_array)->opcodes[check_op_number]; opline->op2.opline_num = get_next_op_number(); SET_NODE(opline->result, result); + + CG(memoize_mode) = orig_memoize_mode; } else { if (!fbc) { zend_string_release_ex(name, 0); diff --git a/Zend/zend_virtual_cwd.h b/Zend/zend_virtual_cwd.h index f819389af33c7..15c3b984a9dc7 100644 --- a/Zend/zend_virtual_cwd.h +++ b/Zend/zend_virtual_cwd.h @@ -87,6 +87,16 @@ typedef unsigned short mode_t; #else #ifdef HAVE_DIRENT_H #include + +#ifndef DT_UNKNOWN +# define DT_UNKNOWN 0 +#endif +#ifndef DT_DIR +# define DT_DIR 4 +#endif +#ifndef DT_REG +# define DT_REG 8 +#endif #endif #define DEFAULT_SLASH '/' diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 4009a5b84df1f..33f51bfa4de9b 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -97,6 +97,14 @@ #if !defined(OPENSSL_NO_EC) && defined(EVP_PKEY_EC) #define HAVE_EVP_PKEY_EC 1 + +/* the OPENSSL_EC_EXPLICIT_CURVE value was added + * in OpenSSL 1.1.0; previous versions should + * use 0 instead. + */ +#ifndef OPENSSL_EC_EXPLICIT_CURVE +#define OPENSSL_EC_EXPLICIT_CURVE 0x000 +#endif #endif ZEND_DECLARE_MODULE_GLOBALS(openssl) @@ -4264,158 +4272,290 @@ static EVP_PKEY *php_openssl_pkey_init_dh(zval *data, bool *is_private) #ifdef HAVE_EVP_PKEY_EC #if PHP_OPENSSL_API_VERSION < 0x30000 static bool php_openssl_pkey_init_legacy_ec(EC_KEY *eckey, zval *data, bool *is_private) { + BIGNUM *p = NULL, *a = NULL, *b = NULL, *order = NULL, *g_x = NULL, *g_y = NULL , *cofactor = NULL; + BIGNUM *x = NULL, *y = NULL, *d = NULL; + EC_POINT *point_g = NULL; + EC_POINT *point_q = NULL; EC_GROUP *group = NULL; - EC_POINT *pnt = NULL; - BIGNUM *d = NULL; - zval *bn; - zval *x; - zval *y; + BN_CTX *bctx = BN_CTX_new(); *is_private = false; - if ((bn = zend_hash_str_find(Z_ARRVAL_P(data), "curve_name", sizeof("curve_name") - 1)) != NULL && - Z_TYPE_P(bn) == IS_STRING) { - int nid = OBJ_sn2nid(Z_STRVAL_P(bn)); - if (nid != NID_undef) { - group = EC_GROUP_new_by_curve_name(nid); - if (!group) { - php_openssl_store_errors(); + zval *curve_name_zv = zend_hash_str_find(Z_ARRVAL_P(data), "curve_name", sizeof("curve_name") - 1); + if (curve_name_zv && Z_TYPE_P(curve_name_zv) == IS_STRING && Z_STRLEN_P(curve_name_zv) > 0) { + int nid = OBJ_sn2nid(Z_STRVAL_P(curve_name_zv)); + if (nid == NID_undef) { + php_error_docref(NULL, E_WARNING, "Unknown elliptic curve (short) name %s", Z_STRVAL_P(curve_name_zv)); + goto clean_exit; + } + + if (!(group = EC_GROUP_new_by_curve_name(nid))) { + goto clean_exit; + } + EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE); + } else { + OPENSSL_PKEY_SET_BN(data, p); + OPENSSL_PKEY_SET_BN(data, a); + OPENSSL_PKEY_SET_BN(data, b); + OPENSSL_PKEY_SET_BN(data, order); + + if (!(p && a && b && order)) { + if (!p && !a && !b && !order) { + php_error_docref(NULL, E_WARNING, "Missing params: curve_name"); + } else { + php_error_docref( + NULL, E_WARNING, "Missing params: curve_name or p, a, b, order"); + } + goto clean_exit; + } + + if (!(group = EC_GROUP_new_curve_GFp(p, a, b, bctx))) { + goto clean_exit; + } + + if (!(point_g = EC_POINT_new(group))) { + goto clean_exit; + } + + zval *generator_zv = zend_hash_str_find(Z_ARRVAL_P(data), "generator", sizeof("generator") - 1); + if (generator_zv && Z_TYPE_P(generator_zv) == IS_STRING && Z_STRLEN_P(generator_zv) > 0) { + if (!(EC_POINT_oct2point(group, point_g, (unsigned char *)Z_STRVAL_P(generator_zv), Z_STRLEN_P(generator_zv), bctx))) { goto clean_exit; } - EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE); - EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED); - if (!EC_KEY_set_group(eckey, group)) { - php_openssl_store_errors(); + } else { + OPENSSL_PKEY_SET_BN(data, g_x); + OPENSSL_PKEY_SET_BN(data, g_y); + + if (!g_x || !g_y) { + php_error_docref( + NULL, E_WARNING, "Missing params: generator or g_x and g_y"); + goto clean_exit; + } + + if (!EC_POINT_set_affine_coordinates_GFp(group, point_g, g_x, g_y, bctx)) { goto clean_exit; } } + + zval *seed_zv = zend_hash_str_find(Z_ARRVAL_P(data), "seed", sizeof("seed") - 1); + if (seed_zv && Z_TYPE_P(seed_zv) == IS_STRING && Z_STRLEN_P(seed_zv) > 0) { + if (!EC_GROUP_set_seed(group, (unsigned char *)Z_STRVAL_P(seed_zv), Z_STRLEN_P(seed_zv))) { + goto clean_exit; + } + } + + /* + * OpenSSL uses 0 cofactor as a marker for "unknown cofactor". + * So accept cofactor == NULL or cofactor >= 0. + * Internally, the lib will check the cofactor value. + */ + OPENSSL_PKEY_SET_BN(data, cofactor); + if (!EC_GROUP_set_generator(group, point_g, order, cofactor)) { + goto clean_exit; + } + EC_GROUP_set_asn1_flag(group, OPENSSL_EC_EXPLICIT_CURVE); } - if (group == NULL) { - php_error_docref(NULL, E_WARNING, "Unknown curve name"); + EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED); + + if (!EC_KEY_set_group(eckey, group)) { goto clean_exit; } - // The public key 'pnt' can be calculated from 'd' or is defined by 'x' and 'y' - if ((bn = zend_hash_str_find(Z_ARRVAL_P(data), "d", sizeof("d") - 1)) != NULL && - Z_TYPE_P(bn) == IS_STRING) { + OPENSSL_PKEY_SET_BN(data, d); + OPENSSL_PKEY_SET_BN(data, x); + OPENSSL_PKEY_SET_BN(data, y); + + if (d) { *is_private = true; - d = BN_bin2bn((unsigned char*) Z_STRVAL_P(bn), Z_STRLEN_P(bn), NULL); if (!EC_KEY_set_private_key(eckey, d)) { - php_openssl_store_errors(); - goto clean_exit; - } - // Calculate the public key by multiplying the Point Q with the public key - // P = d * Q - pnt = EC_POINT_new(group); - if (!pnt || !EC_POINT_mul(group, pnt, d, NULL, NULL, NULL)) { - php_openssl_store_errors(); goto clean_exit; } - BN_free(d); - } else if ((x = zend_hash_str_find(Z_ARRVAL_P(data), "x", sizeof("x") - 1)) != NULL && - Z_TYPE_P(x) == IS_STRING && - (y = zend_hash_str_find(Z_ARRVAL_P(data), "y", sizeof("y") - 1)) != NULL && - Z_TYPE_P(y) == IS_STRING) { - pnt = EC_POINT_new(group); - if (pnt == NULL) { - php_openssl_store_errors(); + point_q = EC_POINT_new(group); + if (!point_q || !EC_POINT_mul(group, point_q, d, NULL, NULL, bctx)) { goto clean_exit; } - if (!EC_POINT_set_affine_coordinates_GFp( - group, pnt, BN_bin2bn((unsigned char*) Z_STRVAL_P(x), Z_STRLEN_P(x), NULL), - BN_bin2bn((unsigned char*) Z_STRVAL_P(y), Z_STRLEN_P(y), NULL), NULL)) { - php_openssl_store_errors(); + } else if (x && y) { + /* OpenSSL does not allow setting EC_PUB_X/EC_PUB_Y, so convert to encoded format. */ + point_q = EC_POINT_new(group); + if (!point_q || !EC_POINT_set_affine_coordinates_GFp(group, point_q, x, y, bctx)) { goto clean_exit; } } - if (pnt != NULL) { - if (!EC_KEY_set_public_key(eckey, pnt)) { - php_openssl_store_errors(); + if (point_q != NULL) { + if (!EC_KEY_set_public_key(eckey, point_q)) { goto clean_exit; } - EC_POINT_free(pnt); - pnt = NULL; } if (!EC_KEY_check_key(eckey)) { *is_private = true; PHP_OPENSSL_RAND_ADD_TIME(); EC_KEY_generate_key(eckey); - php_openssl_store_errors(); - } - if (EC_KEY_check_key(eckey)) { - EC_GROUP_free(group); - return true; - } else { - php_openssl_store_errors(); } clean_exit: - BN_free(d); - EC_POINT_free(pnt); + php_openssl_store_errors(); + BN_CTX_free(bctx); EC_GROUP_free(group); - return false; + EC_POINT_free(point_g); + EC_POINT_free(point_q); + BN_free(p); + BN_free(a); + BN_free(b); + BN_free(order); + BN_free(g_x); + BN_free(g_y); + BN_free(cofactor); + BN_free(d); + BN_free(x); + BN_free(y); + return EC_KEY_check_key(eckey); } #endif static EVP_PKEY *php_openssl_pkey_init_ec(zval *data, bool *is_private) { #if PHP_OPENSSL_API_VERSION >= 0x30000 - BIGNUM *d = NULL, *x = NULL, *y = NULL; + int nid = NID_undef; + BIGNUM *p = NULL, *a = NULL, *b = NULL, *order = NULL, *g_x = NULL, *g_y = NULL, *cofactor = NULL; + BIGNUM *x = NULL, *y = NULL, *d = NULL; + EC_POINT *point_g = NULL; + EC_POINT *point_q = NULL; + unsigned char *point_g_buf = NULL; + unsigned char *point_q_buf = NULL; EC_GROUP *group = NULL; - EC_POINT *pnt = NULL; - unsigned char *pnt_oct = NULL; EVP_PKEY *param_key = NULL, *pkey = NULL; EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + BN_CTX *bctx = BN_CTX_new(); OSSL_PARAM *params = NULL; OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new(); - zval *curve_name_zv = zend_hash_str_find(Z_ARRVAL_P(data), "curve_name", sizeof("curve_name") - 1); - - OPENSSL_PKEY_SET_BN(data, d); - OPENSSL_PKEY_SET_BN(data, x); - OPENSSL_PKEY_SET_BN(data, y); *is_private = false; - if (!ctx || !bld || !curve_name_zv || Z_TYPE_P(curve_name_zv) != IS_STRING) { - goto cleanup; - } + zval *curve_name_zv = zend_hash_str_find(Z_ARRVAL_P(data), "curve_name", sizeof("curve_name") - 1); + if (curve_name_zv && Z_TYPE_P(curve_name_zv) == IS_STRING && Z_STRLEN_P(curve_name_zv) > 0) { + nid = OBJ_sn2nid(Z_STRVAL_P(curve_name_zv)); + if (nid == NID_undef) { + php_error_docref(NULL, E_WARNING, "Unknown elliptic curve (short) name %s", Z_STRVAL_P(curve_name_zv)); + goto cleanup; + } - int nid = OBJ_sn2nid(Z_STRVAL_P(curve_name_zv)); - group = EC_GROUP_new_by_curve_name(nid); - if (!group) { - php_error_docref(NULL, E_WARNING, "Unknown curve name"); - goto cleanup; - } + if (!(group = EC_GROUP_new_by_curve_name(nid))) { + goto cleanup; + } - OSSL_PARAM_BLD_push_utf8_string( - bld, OSSL_PKEY_PARAM_GROUP_NAME, Z_STRVAL_P(curve_name_zv), Z_STRLEN_P(curve_name_zv)); + if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, Z_STRVAL_P(curve_name_zv), Z_STRLEN_P(curve_name_zv))) { + goto cleanup; + } + } else { + OPENSSL_PKEY_SET_BN(data, p); + OPENSSL_PKEY_SET_BN(data, a); + OPENSSL_PKEY_SET_BN(data, b); + OPENSSL_PKEY_SET_BN(data, order); + + if (!(p && a && b && order)) { + if (!p && !a && !b && !order) { + php_error_docref(NULL, E_WARNING, "Missing params: curve_name"); + } else { + php_error_docref(NULL, E_WARNING, "Missing params: curve_name or p, a, b, order"); + } + goto cleanup; + } - if (d) { - OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, d); + if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_P, p) || + !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_A, a) || + !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_B, b) || + !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_ORDER, order) || + !OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_EC_FIELD_TYPE, SN_X9_62_prime_field, 0)) { + goto cleanup; + } - pnt = EC_POINT_new(group); - if (!pnt || !EC_POINT_mul(group, pnt, d, NULL, NULL, NULL)) { + if (!(group = EC_GROUP_new_curve_GFp(p, a, b, bctx))) { goto cleanup; } - } else if (x && y) { - /* OpenSSL does not allow setting EC_PUB_X/EC_PUB_Y, so convert to encoded format. */ - pnt = EC_POINT_new(group); - if (!pnt || !EC_POINT_set_affine_coordinates(group, pnt, x, y, NULL)) { + + if (!(point_g = EC_POINT_new(group))) { goto cleanup; } - } - if (pnt) { - size_t pnt_oct_len = - EC_POINT_point2buf(group, pnt, POINT_CONVERSION_COMPRESSED, &pnt_oct, NULL); - if (!pnt_oct_len) { + zval *generator_zv = zend_hash_str_find(Z_ARRVAL_P(data), "generator", sizeof("generator") - 1); + if (generator_zv && Z_TYPE_P(generator_zv) == IS_STRING && Z_STRLEN_P(generator_zv) > 0) { + if (!EC_POINT_oct2point(group, point_g, (unsigned char *)Z_STRVAL_P(generator_zv), Z_STRLEN_P(generator_zv), bctx) || + !OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_GENERATOR, Z_STRVAL_P(generator_zv), Z_STRLEN_P(generator_zv))) { + goto cleanup; + } + } else { + OPENSSL_PKEY_SET_BN(data, g_x); + OPENSSL_PKEY_SET_BN(data, g_y); + + if (!g_x || !g_y) { + php_error_docref( + NULL, E_WARNING, "Missing params: generator or g_x and g_y"); + goto cleanup; + } + + if (!EC_POINT_set_affine_coordinates(group, point_g, g_x, g_y, bctx)) { + goto cleanup; + } + + size_t point_g_buf_len = + EC_POINT_point2buf(group, point_g, POINT_CONVERSION_COMPRESSED, &point_g_buf, bctx); + if (!point_g_buf_len) { + goto cleanup; + } + + if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_GENERATOR, point_g_buf, point_g_buf_len)) { + goto cleanup; + } + } + + zval *seed_zv = zend_hash_str_find(Z_ARRVAL_P(data), "seed", sizeof("seed") - 1); + if (seed_zv && Z_TYPE_P(seed_zv) == IS_STRING && Z_STRLEN_P(seed_zv) > 0) { + if (!EC_GROUP_set_seed(group, (unsigned char *)Z_STRVAL_P(seed_zv), Z_STRLEN_P(seed_zv)) || + !OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_SEED, Z_STRVAL_P(seed_zv), Z_STRLEN_P(seed_zv))) { + goto cleanup; + } + } + + OPENSSL_PKEY_SET_BN(data, cofactor); + if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR, cofactor) || + !EC_GROUP_set_generator(group, point_g, order, cofactor)) { goto cleanup; } - OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pnt_oct, pnt_oct_len); + nid = EC_GROUP_check_named_curve(group, 0, bctx); + } + + /* custom params not supported with SM2, SKIP */ + if (nid != NID_sm2) { + OPENSSL_PKEY_SET_BN(data, d); + OPENSSL_PKEY_SET_BN(data, x); + OPENSSL_PKEY_SET_BN(data, y); + + if (d) { + point_q = EC_POINT_new(group); + if (!point_q || !EC_POINT_mul(group, point_q, d, NULL, NULL, bctx) || + !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, d)) { + goto cleanup; + } + } else if (x && y) { + /* OpenSSL does not allow setting EC_PUB_X/EC_PUB_Y, so convert to encoded format. */ + point_q = EC_POINT_new(group); + if (!point_q || !EC_POINT_set_affine_coordinates(group, point_q, x, y, bctx)) { + goto cleanup; + } + } + + if (point_q) { + size_t point_q_buf_len = + EC_POINT_point2buf(group, point_q, POINT_CONVERSION_COMPRESSED, &point_q_buf, bctx); + if (!point_q_buf_len || + !OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, point_q_buf, point_q_buf_len)) { + goto cleanup; + } + } } params = OSSL_PARAM_BLD_to_param(bld); @@ -4423,21 +4563,25 @@ static EVP_PKEY *php_openssl_pkey_init_ec(zval *data, bool *is_private) { goto cleanup; } - if (EVP_PKEY_fromdata_init(ctx) <= 0 || + if (d || (x && y)) { + if (EVP_PKEY_fromdata_init(ctx) <= 0 || EVP_PKEY_fromdata(ctx, ¶m_key, EVP_PKEY_KEYPAIR, params) <= 0) { - goto cleanup; + goto cleanup; + } + EVP_PKEY_CTX_free(ctx); + ctx = EVP_PKEY_CTX_new(param_key, NULL); } - - EVP_PKEY_CTX_free(ctx); - ctx = EVP_PKEY_CTX_new(param_key, NULL); - if (EVP_PKEY_check(ctx)) { + + if (EVP_PKEY_check(ctx) || EVP_PKEY_public_check_quick(ctx)) { *is_private = d != NULL; EVP_PKEY_up_ref(param_key); pkey = param_key; } else { *is_private = true; PHP_OPENSSL_RAND_ADD_TIME(); - if (EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_keygen(ctx, &pkey) <= 0) { + if (EVP_PKEY_keygen_init(ctx) != 1 || + EVP_PKEY_CTX_set_params(ctx, params) != 1 || + EVP_PKEY_generate(ctx, &pkey) != 1) { goto cleanup; } } @@ -4446,11 +4590,21 @@ static EVP_PKEY *php_openssl_pkey_init_ec(zval *data, bool *is_private) { php_openssl_store_errors(); EVP_PKEY_free(param_key); EVP_PKEY_CTX_free(ctx); + BN_CTX_free(bctx); OSSL_PARAM_free(params); OSSL_PARAM_BLD_free(bld); - EC_POINT_free(pnt); EC_GROUP_free(group); - OPENSSL_free(pnt_oct); + EC_POINT_free(point_g); + EC_POINT_free(point_q); + OPENSSL_free(point_g_buf); + OPENSSL_free(point_q_buf); + BN_free(p); + BN_free(a); + BN_free(b); + BN_free(order); + BN_free(g_x); + BN_free(g_y); + BN_free(cofactor); BN_free(d); BN_free(x); BN_free(y); @@ -4796,7 +4950,21 @@ PHP_FUNCTION(openssl_pkey_get_details) */ #if PHP_OPENSSL_API_VERSION >= 0x30000 zval ary; - switch (EVP_PKEY_base_id(pkey)) { + int base_id = 0; + + if (EVP_PKEY_id(pkey) != EVP_PKEY_KEYMGMT) { + base_id = EVP_PKEY_base_id(pkey); + } else { + const char *type_name = EVP_PKEY_get0_type_name(pkey); + if (type_name) { + int nid = OBJ_txt2nid(type_name); + if (nid != NID_undef) { + base_id = EVP_PKEY_type(nid); + } + } + } + + switch (base_id) { case EVP_PKEY_RSA: ktype = OPENSSL_KEYTYPE_RSA; array_init(&ary); @@ -4859,7 +5027,9 @@ PHP_FUNCTION(openssl_pkey_get_details) break; } #endif - EMPTY_SWITCH_DEFAULT_CASE(); + default: + ktype = -1; + break; } #else switch (EVP_PKEY_base_id(pkey)) { @@ -4955,24 +5125,22 @@ PHP_FUNCTION(openssl_pkey_get_details) ec_group = EC_KEY_get0_group(ec_key); - // Curve nid (numerical identifier) used for ASN1 mapping - nid = EC_GROUP_get_curve_name(ec_group); - if (nid == NID_undef) { - break; - } array_init(&ec); - // Short object name - crv_sn = (char*) OBJ_nid2sn(nid); - if (crv_sn != NULL) { - add_assoc_string(&ec, "curve_name", crv_sn); - } + /** Curve nid (numerical identifier) used for ASN1 mapping */ + nid = EC_GROUP_get_curve_name(ec_group); + if (nid != NID_undef) { + crv_sn = (char*) OBJ_nid2sn(nid); + if (crv_sn != NULL) { + add_assoc_string(&ec, "curve_name", crv_sn); + } - obj = OBJ_nid2obj(nid); - if (obj != NULL) { - int oir_len = OBJ_obj2txt(oir_buf, sizeof(oir_buf), obj, 1); - add_assoc_stringl(&ec, "curve_oid", (char*) oir_buf, oir_len); - ASN1_OBJECT_free(obj); + obj = OBJ_nid2obj(nid); + if (obj != NULL) { + int oir_len = OBJ_obj2txt(oir_buf, sizeof(oir_buf), obj, 1); + add_assoc_stringl(&ec, "curve_oid", (char*) oir_buf, oir_len); + ASN1_OBJECT_free(obj); + } } pub = EC_KEY_get0_public_key(ec_key); diff --git a/ext/openssl/tests/ecc.phpt b/ext/openssl/tests/ecc.phpt index a881d59c9a2be..c5a6f2899898c 100644 --- a/ext/openssl/tests/ecc.phpt +++ b/ext/openssl/tests/ecc.phpt @@ -74,7 +74,8 @@ var_dump($csr); $pubkey1 = openssl_pkey_get_details(openssl_csr_get_public_key($csr)); var_dump(isset($pubkey1["ec"]["priv_key"])); unset($d1["ec"]["priv_key"]); -var_dump(array_diff($d1["ec"], $pubkey1["ec"])); +$diff = array_diff($d1["ec"], $pubkey1["ec"]); +var_dump(isset($diff["d"]) && is_string($diff["d"]) && strlen($diff["d"]) > 0); $x509 = openssl_csr_sign($csr, null, $key1, 365, $args); var_dump($x509); @@ -121,10 +122,7 @@ Testing openssl_csr_new with existing ecc key object(OpenSSLCertificateSigningRequest)#%d (0) { } bool(false) -array(1) { - ["d"]=> - string(%d) "%a" -} +bool(true) object(OpenSSLCertificate)#%d (0) { } Testing openssl_x509_check_private_key diff --git a/ext/openssl/tests/ecc_custom_params.phpt b/ext/openssl/tests/ecc_custom_params.phpt new file mode 100644 index 0000000000000..0c63af1450ba6 --- /dev/null +++ b/ext/openssl/tests/ecc_custom_params.phpt @@ -0,0 +1,113 @@ +--TEST-- +openssl_*() with OPENSSL_KEYTYPE_EC for ec custom params +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + array( + 'curve_name' => $curve_name, + ) +)); + +var_dump($ec); +$details = openssl_pkey_get_details($ec); +$ec_details = $details['ec']; +var_dump($ec_details['curve_name'] === $curve_name); + +// EC - generate keypair from priv_key "d" with explicit parameters (OSCCA WAPIP192v1 Elliptic curve) +echo "Testing openssl_pkey_new with ec explicit parameters\n"; +$d = hex2bin('8D0AC65AAEA0D6B96254C65817D4A143A9E7A03876F1A37D'); +$x = hex2bin('98E07AAD50C31F9189EBE6B8B5C70E5DEE59D7A8BC344CC6'); +$y = hex2bin('6109D3D96E52D0867B9D05D72D07BE5876A3D973E0E96792'); +$p = hex2bin('BDB6F4FE3E8B1D9E0DA8C0D46F4C318CEFE4AFE3B6B8551F'); +$a = hex2bin('BB8E5E8FBC115E139FE6A814FE48AAA6F0ADA1AA5DF91985'); +$b = hex2bin('1854BEBDC31B21B7AEFC80AB0ECD10D5B1B3308E6DBF11C1'); +$g_x = hex2bin('4AD5F7048DE709AD51236DE65E4D4B482C836DC6E4106640'); +$g_y = hex2bin('02BB3A02D4AAADACAE24817A4CA3A1B014B5270432DB27D2'); +$order = hex2bin('BDB6F4FE3E8B1D9E0DA8C0D40FC962195DFAE76F56564677'); + +$ec = openssl_pkey_new(array( + 'ec'=> array( + 'p' => $p, + 'a' => $a, + 'b' => $b, + 'order' => $order, + 'g_x' => $g_x, + 'g_y' => $g_y, + 'd' => $d, + ) +)); + +$details = openssl_pkey_get_details($ec); +$ec_details = $details['ec']; +var_dump($ec_details['x'] === $x); +var_dump($ec_details['y'] === $y); +var_dump($ec_details['d'] === $d); + +echo "Testing openssl_pkey_new with ec missing params \n"; +// EC - invalid curve_name +$ec = openssl_pkey_new(array( + 'ec'=> array( + 'curve_name' => 'invalid_curve_name', + ) +)); +var_dump($ec); + +// EC - missing all params +$ec = openssl_pkey_new(array( + 'ec'=> array() +)); +var_dump($ec); + +// EC - missing "p" param +$ec = openssl_pkey_new(array( + 'ec'=> array( + 'a' => $a, + 'b' => $b, + 'order' => $order + ) +)); +var_dump($ec); + +// EC - missing "generator" or "g_x" and "g_y" param +$ec = openssl_pkey_new(array( + 'ec'=> array( + 'p' => $p, + 'a' => $a, + 'b' => $b, + 'order' => $order + ) +)); +var_dump($ec); +?> +--EXPECTF-- +Testing openssl_pkey_new with ec curve_name +object(OpenSSLAsymmetricKey)#%d (0) { +} +bool(true) +Testing openssl_pkey_new with ec explicit parameters +bool(true) +bool(true) +bool(true) +Testing openssl_pkey_new with ec missing params + +Warning: openssl_pkey_new(): Unknown elliptic curve (short) name invalid_curve_name in %s on line %d +bool(false) + +Warning: openssl_pkey_new(): Missing params: curve_name in %s on line %d +bool(false) + +Warning: openssl_pkey_new(): Missing params: curve_name or p, a, b, order in %s on line %d +bool(false) + +Warning: openssl_pkey_new(): Missing params: generator or g_x and g_y in %s on line %d +bool(false) diff --git a/ext/openssl/tests/ecc_sm2.phpt b/ext/openssl/tests/ecc_sm2.phpt new file mode 100644 index 0000000000000..11d4c22ae5b26 --- /dev/null +++ b/ext/openssl/tests/ecc_sm2.phpt @@ -0,0 +1,92 @@ +--TEST-- +openssl_*() with OPENSSL_KEYTYPE_EC for SM2 +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + array( + 'curve_name' => 'SM2', + ) +)); + +var_dump($ec); +$details = openssl_pkey_get_details($ec); +var_dump($details["bits"]); +var_dump(strlen($details["key"])); +var_dump($details["ec"]["curve_name"]); +var_dump($details["type"] == OPENSSL_KEYTYPE_EC); + +// EC - generate SM2 keypair with curve_name +echo "Testing openssl_pkey_get_public from SM2 pem pubkey\n"; +$public_key = openssl_pkey_get_public($details["key"]); +var_dump($public_key); +$details_public_key = openssl_pkey_get_details($public_key); +var_dump(strlen($details["key"])); +var_dump($details_public_key["ec"]["curve_name"]); +var_dump($details["type"] == OPENSSL_KEYTYPE_EC); +var_dump($details_public_key["ec"]["x"] === $details["ec"]["x"]); +var_dump($details_public_key["ec"]["y"] === $details["ec"]["y"]); + +// EC - generate keypair with explicit parameters (SM2 curve) +echo "Testing openssl_pkey_new with ec explicit parameters (SM2 curve)\n"; +$p = hex2bin('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF'); +$a = hex2bin('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC'); +$b = hex2bin('28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93'); +$g_x = hex2bin('32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7'); +$g_y = hex2bin('BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0'); +$order = hex2bin('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123'); + +$ec = openssl_pkey_new(array( + 'ec'=> array( + 'p' => $p, + 'a' => $a, + 'b' => $b, + 'order' => $order, + 'g_x' => $g_x, + 'g_y' => $g_y + ) +)); + +$details = openssl_pkey_get_details($ec); +var_dump($details['bits']); +var_dump(strlen($details['key'])); +var_dump($details['type'] == OPENSSL_KEYTYPE_EC); +$public_key = openssl_pkey_get_public($details["key"]); +$details_public_key = openssl_pkey_get_details($public_key); +var_dump(strlen($details["key"])); +var_dump($details["type"] == OPENSSL_KEYTYPE_EC); +var_dump($details_public_key["ec"]["x"] === $details["ec"]["x"]); +var_dump($details_public_key["ec"]["y"] === $details["ec"]["y"]); +?> +--EXPECTF-- +Testing openssl_pkey_new with ec curve_name SM2 +object(OpenSSLAsymmetricKey)#%d (0) { +} +int(256) +int(178) +string(3) "SM2" +bool(true) +Testing openssl_pkey_get_public from SM2 pem pubkey +object(OpenSSLAsymmetricKey)#%d (0) { +} +int(178) +string(3) "SM2" +bool(true) +bool(true) +bool(true) +Testing openssl_pkey_new with ec explicit parameters (SM2 curve) +int(256) +int(475) +bool(true) +int(475) +bool(true) +bool(true) +bool(true) diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index ede5397bb51d7..d7fda32de0422 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -108,6 +108,7 @@ static ssize_t phar_dir_read(php_stream *stream, char *buf, size_t count) /* {{{ memset(buf, 0, sizeof(php_stream_dirent)); memcpy(((php_stream_dirent *) buf)->d_name, ZSTR_VAL(str_key), to_read); ((php_stream_dirent *) buf)->d_name[to_read + 1] = '\0'; + ((php_stream_dirent *) buf)->d_type = DT_UNKNOWN; return sizeof(php_stream_dirent); } diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index 378f707f44c3b..1b29dce787a08 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -759,8 +759,6 @@ static void spl_filesystem_object_construct(INTERNAL_FUNCTION_PARAMETERS, zend_l } zend_restore_error_handling(&error_handling); - - intern->u.dir.is_recursive = instanceof_function(intern->std.ce, spl_ce_RecursiveDirectoryIterator) ? 1 : 0; } /* }}} */ @@ -1487,6 +1485,11 @@ PHP_METHOD(RecursiveDirectoryIterator, hasChildren) if (spl_filesystem_is_invalid_or_dot(intern->u.dir.entry.d_name)) { RETURN_FALSE; } else { + if (intern->u.dir.entry.d_type == DT_DIR) { + RETURN_TRUE; + } else if (intern->u.dir.entry.d_type == DT_REG) { + RETURN_FALSE; + } if (spl_filesystem_object_get_file_name(intern) == FAILURE) { RETURN_THROWS(); } diff --git a/ext/spl/spl_directory.h b/ext/spl/spl_directory.h index f94ac670d4fa0..d036013161cb1 100644 --- a/ext/spl/spl_directory.h +++ b/ext/spl/spl_directory.h @@ -64,7 +64,6 @@ struct _spl_filesystem_object { php_stream *dirp; zend_string *sub_path; int index; - int is_recursive; zend_function *func_rewind; zend_function *func_next; zend_function *func_valid; diff --git a/ext/standard/ftp_fopen_wrapper.c b/ext/standard/ftp_fopen_wrapper.c index c3b107be65527..1757921d3d47f 100644 --- a/ext/standard/ftp_fopen_wrapper.c +++ b/ext/standard/ftp_fopen_wrapper.c @@ -629,6 +629,7 @@ static ssize_t php_ftp_dirstream_read(php_stream *stream, char *buf, size_t coun memcpy(ent->d_name, ZSTR_VAL(basename), tmp_len); ent->d_name[tmp_len - 1] = '\0'; zend_string_release_ex(basename, 0); + ent->d_type = DT_UNKNOWN; /* Trim off trailing whitespace characters */ while (tmp_len > 0 && diff --git a/ext/standard/tests/hrtime/hrtime.phpt b/ext/standard/tests/hrtime/hrtime.phpt index 795b78c1dcb37..614056c7d3364 100644 --- a/ext/standard/tests/hrtime/hrtime.phpt +++ b/ext/standard/tests/hrtime/hrtime.phpt @@ -1,6 +1,6 @@ --TEST-- Test hrtime() aligns with microtime() ---XFAIL-- +--FLAKY-- This test frequently fails in CI --FILE-- glob.gl_pathv[index], pglob->flags & GLOB_APPEND, &path); ++pglob->index; PHP_STRLCPY(ent->d_name, path, sizeof(ent->d_name), strlen(path)); + ent->d_type = DT_UNKNOWN; return sizeof(php_stream_dirent); } pglob->index = glob_result_count; diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 128c3410aa835..86c517132482e 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -1032,6 +1032,11 @@ static ssize_t php_plain_files_dirstream_read(php_stream *stream, char *buf, siz result = readdir(dir); if (result) { PHP_STRLCPY(ent->d_name, result->d_name, sizeof(ent->d_name), strlen(result->d_name)); +#ifdef _DIRENT_HAVE_D_TYPE + ent->d_type = result->d_type; +#else + ent->d_type = DT_UNKNOWN; +#endif return sizeof(php_stream_dirent); } return 0; diff --git a/main/streams/userspace.c b/main/streams/userspace.c index 165bd7da3ad98..c7e57ab3ba078 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -1320,6 +1320,7 @@ static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t co if (call_result == SUCCESS && Z_TYPE(retval) != IS_FALSE && Z_TYPE(retval) != IS_TRUE) { convert_to_string(&retval); PHP_STRLCPY(ent->d_name, Z_STRVAL(retval), sizeof(ent->d_name), Z_STRLEN(retval)); + ent->d_type = DT_UNKNOWN; didread = sizeof(php_stream_dirent); } else if (call_result == FAILURE) { diff --git a/run-tests.php b/run-tests.php index a112e8f5347f0..b26d214390953 100755 --- a/run-tests.php +++ b/run-tests.php @@ -2650,7 +2650,7 @@ function run_test(string $php, $file, array $env): string $wanted_re = null; } - if (!$passed && !$retried && $retriable && error_may_be_retried($output)) { + if (!$passed && !$retried && $retriable && error_may_be_retried($test, $output)) { $retried = true; goto retry; } @@ -2832,9 +2832,10 @@ function run_test(string $php, $file, array $env): string return $restype[0] . 'ED'; } -function error_may_be_retried(string $output): bool +function error_may_be_retried(TestFile $test, string $output): bool { - return preg_match('((timed out)|(connection refused)|(404: page not found)|(address already in use)|(mailbox already exists))i', $output) === 1; + return preg_match('((timed out)|(connection refused)|(404: page not found)|(address already in use)|(mailbox already exists))i', $output) === 1 + || $test->hasSection('FLAKY'); } function expectf_to_regex(?string $wanted): string @@ -3727,6 +3728,7 @@ class TestFile 'INI', 'ENV', 'EXTENSIONS', 'SKIPIF', 'XFAIL', 'XLEAK', 'CLEAN', 'CREDITS', 'DESCRIPTION', 'CONFLICTS', 'WHITESPACE_SENSITIVE', + 'FLAKY', ]; /** diff --git a/win32/readdir.c b/win32/readdir.c index 2c3344381913c..89381e5834b62 100644 --- a/win32/readdir.c +++ b/win32/readdir.c @@ -120,6 +120,13 @@ struct dirent *readdir(DIR *dp) dp->dent.d_ino = 1; dp->dent.d_off = dp->offset; + if (dp->fileinfo.dwFileAttributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DEVICE)) { + dp->dent.d_type = DT_UNKNOWN; /* conservative */ + } else if (dp->fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + dp->dent.d_type = DT_DIR; + } else { + dp->dent.d_type = DT_REG; + } return &(dp->dent); }/*}}}*/ diff --git a/win32/readdir.h b/win32/readdir.h index cc8e1a9a2510c..4d5a7ce476f0e 100644 --- a/win32/readdir.h +++ b/win32/readdir.h @@ -15,11 +15,17 @@ extern "C" { #include "ioutil.h" +#define _DIRENT_HAVE_D_TYPE +#define DT_UNKNOWN 0 +#define DT_DIR 4 +#define DT_REG 8 + /* struct dirent - same as Unix */ struct dirent { long d_ino; /* inode (always 1 in WIN32) */ off_t d_off; /* offset to this dirent */ unsigned short d_reclen; /* length of d_name */ + unsigned char d_type; char d_name[1]; /* null terminated filename in the current encoding, glyph number <= 255 wchar_t's + \0 byte */ };