[PHP-CVS] [php-src] master: openssl: Fix checks on BIO_get_mem_ptr() (#20998)

[email protected] (Nora Dossche via GitHub) Mon, 3 Aug 2026 18:41:33 +0000
Newsgroups php.cvs
Message-ID <[email protected]>
Author: Nora Dossche (ndossche)
Committer: GitHub (web-flow)
Pusher: ndossche
Date: 2026-08-03T20:41:29+02:00

Commit: https://github.com/php/php-src/commit/9d7480ee2ec3f6c17e658069fac178f7719b6cfb
Raw diff: https://github.com/php/php-src/commit/9d7480ee2ec3f6c17e658069fac178f7719b6cfb.diff

openssl: Fix checks on BIO_get_mem_ptr() (#20998)

If the call fails, then the pointer remains uninitialized, and this
triggers undefined behaviour or the reading of a dangling pointer.
In my own tests this came out as a UAF. However, it seems not
exploitable by an attacker as failure should be not controllable.

It's worth pointing out that OpenSSL checks the return value in its own
code that calls this function as well [1].

[1] https://github.com/openssl/openssl/blob/b2ecef451ccede07366023da4553f113f6e4fe71/apps/lib/apps.c#L3307-L3311

Changed paths:
  M  ext/openssl/openssl.c


Diff:

diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c
index 55a437c0865a..80c0a8bab073 100644
--- a/ext/openssl/openssl.c
+++ b/ext/openssl/openssl.c
@@ -1129,6 +1129,7 @@ PHP_FUNCTION(openssl_spki_export)
 	EVP_PKEY *pkey = NULL;
 	NETSCAPE_SPKI *spki = NULL;
 	BIO *out = NULL;
+	BUF_MEM *bio_buf;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &spkstr, &spkstr_len) == FAILURE) {
 		RETURN_THROWS();
@@ -1158,10 +1159,7 @@ PHP_FUNCTION(openssl_spki_export)
 	}
 
 	out = BIO_new(BIO_s_mem());
-	if (out && PEM_write_bio_PUBKEY(out, pkey)) {
-		BUF_MEM *bio_buf;
-
-		BIO_get_mem_ptr(out, &bio_buf);
+	if (out && PEM_write_bio_PUBKEY(out, pkey) && BIO_get_mem_ptr(out, &bio_buf) > 0) {
 		RETVAL_STRINGL((char *)bio_buf->data, bio_buf->length);
 	} else {
 		php_openssl_store_errors();
@@ -1232,6 +1230,7 @@ PHP_FUNCTION(openssl_x509_export)
 	zval *zout;
 	bool notext = 1;
 	BIO * bio_out;
+	BUF_MEM *bio_buf;
 
 	ZEND_PARSE_PARAMETERS_START(2, 3)
 		Z_PARAM_OBJ_OF_CLASS_OR_STR(cert_obj, php_openssl_certificate_ce, cert_str)
@@ -1255,10 +1254,7 @@ PHP_FUNCTION(openssl_x509_export)
 	}
 	if (!notext && !X509_print(bio_out, cert)) {
 		php_openssl_store_errors();
-	} else if (PEM_write_bio_X509(bio_out, cert)) {
-		BUF_MEM *bio_buf;
-
-		BIO_get_mem_ptr(bio_out, &bio_buf);
+	} else if (PEM_write_bio_X509(bio_out, cert) && BIO_get_mem_ptr(bio_out, &bio_buf) > 0) {
 		ZEND_TRY_ASSIGN_REF_STRINGL(zout, bio_buf->data, bio_buf->length);
 
 		RETVAL_TRUE;
@@ -1539,8 +1535,7 @@ PHP_FUNCTION(openssl_x509_parse)
 			goto err_subitem;
 		}
 		if (nid == NID_subject_alt_name) {
-			if (openssl_x509v3_subjectAltName(bio_out, extension) == 0) {
-				BIO_get_mem_ptr(bio_out, &bio_buf);
+			if (openssl_x509v3_subjectAltName(bio_out, extension) == 0 && BIO_get_mem_ptr(bio_out, &bio_buf) > 0) {
 				add_assoc_stringl(&subitem, extname, bio_buf->data, bio_buf->length);
 			} else {
 				BIO_free(bio_out);
@@ -1548,7 +1543,10 @@ PHP_FUNCTION(openssl_x509_parse)
 			}
 		}
 		else if (X509V3_EXT_print(bio_out, extension, 0, 0) > 0) {
-			BIO_get_mem_ptr(bio_out, &bio_buf);
+			if (BIO_get_mem_ptr(bio_out, &bio_buf) <= 0) {
+				BIO_free(bio_out);
+				goto err_subitem;
+			}
 			add_assoc_stringl(&subitem, extname, bio_buf->data, bio_buf->length);
 		} else {
 			php_openssl_add_assoc_asn1_string(&subitem, extname, X509_EXTENSION_get_data(extension));
@@ -1854,11 +1852,9 @@ PHP_FUNCTION(openssl_pkcs12_export)
 	p12 = PKCS12_create(pass, friendly_name, priv_key, cert, ca, 0, 0, 0, 0, 0);
 
 	if (p12 != NULL) {
+		BUF_MEM *bio_buf;
 		bio_out = BIO_new(BIO_s_mem());
-		if (bio_out && i2d_PKCS12_bio(bio_out, p12)) {
-			BUF_MEM *bio_buf;
-
-			BIO_get_mem_ptr(bio_out, &bio_buf);
+		if (bio_out && i2d_PKCS12_bio(bio_out, p12) && BIO_get_mem_ptr(bio_out, &bio_buf) > 0) {
 			ZEND_TRY_ASSIGN_REF_STRINGL(zout, bio_buf->data, bio_buf->length);
 
 			RETVAL_TRUE;
@@ -1920,10 +1916,9 @@ PHP_FUNCTION(openssl_pkcs12_read)
 		}
 
 		if (cert) {
+			BUF_MEM *bio_buf;
 			bio_out = BIO_new(BIO_s_mem());
-			if (bio_out && PEM_write_bio_X509(bio_out, cert)) {
-				BUF_MEM *bio_buf;
-				BIO_get_mem_ptr(bio_out, &bio_buf);
+			if (bio_out && PEM_write_bio_X509(bio_out, cert) && BIO_get_mem_ptr(bio_out, &bio_buf) > 0) {
 				ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
 				add_assoc_zval(zout, "cert", &zcert);
 			} else {
@@ -1933,14 +1928,13 @@ PHP_FUNCTION(openssl_pkcs12_read)
 		}
 
 		if (pkey) {
+			BUF_MEM *bio_buf;
 			bio_out = BIO_new(BIO_s_mem());
 			if (!bio_out) {
 				goto cleanup;
 			}
 
-			if (PEM_write_bio_PrivateKey(bio_out, pkey, NULL, NULL, 0, 0, NULL)) {
-				BUF_MEM *bio_buf;
-				BIO_get_mem_ptr(bio_out, &bio_buf);
+			if (PEM_write_bio_PrivateKey(bio_out, pkey, NULL, NULL, 0, 0, NULL) && BIO_get_mem_ptr(bio_out, &bio_buf) > 0) {
 				ZVAL_STRINGL(&zpkey, bio_buf->data, bio_buf->length);
 				add_assoc_zval(zout, "pkey", &zpkey);
 			} else {
@@ -1959,12 +1953,11 @@ PHP_FUNCTION(openssl_pkcs12_read)
 
 			for (i = 0; i < cert_num; i++) {
 				zval zextracert;
+				BUF_MEM *bio_buf;
 				X509* aCA = sk_X509_pop(ca);
 				if (!aCA) break;
 
-				if (PEM_write_bio_X509(bio_out, aCA)) {
-					BUF_MEM *bio_buf;
-					BIO_get_mem_ptr(bio_out, &bio_buf);
+				if (PEM_write_bio_X509(bio_out, aCA) && BIO_get_mem_ptr(bio_out, &bio_buf) > 0) {
 					ZVAL_STRINGL(&zextracert, bio_buf->data, bio_buf->length);
 					add_index_zval(&zextracerts, i, &zextracert);
 				}
@@ -2061,6 +2054,7 @@ PHP_FUNCTION(openssl_csr_export)
 	zval *zout;
 	bool notext = 1;
 	BIO * bio_out;
+	BUF_MEM *bio_buf;
 
 	ZEND_PARSE_PARAMETERS_START(2, 3)
 		Z_PARAM_OBJ_OF_CLASS_OR_STR(csr_obj, php_openssl_request_ce, csr_str)
@@ -2082,10 +2076,7 @@ PHP_FUNCTION(openssl_csr_export)
 	bio_out = BIO_new(BIO_s_mem());
 	if (!notext && !X509_REQ_print(bio_out, csr)) {
 		php_openssl_store_errors();
-	} else if (PEM_write_bio_X509_REQ(bio_out, csr)) {
-		BUF_MEM *bio_buf;
-
-		BIO_get_mem_ptr(bio_out, &bio_buf);
+	} else if (PEM_write_bio_X509_REQ(bio_out, csr) && BIO_get_mem_ptr(bio_out, &bio_buf) > 0) {
 		ZEND_TRY_ASSIGN_REF_STRINGL(zout, bio_buf->data, bio_buf->length);
 
 		RETVAL_TRUE;
@@ -3243,11 +3234,10 @@ PHP_FUNCTION(openssl_pkcs7_read)
 			goto clean_exit;
 		}
 		for (i = 0; i < sk_X509_num(certs); i++) {
+			BUF_MEM *bio_buf;
 			X509* ca = sk_X509_value(certs, i);
 
-			if (PEM_write_bio_X509(bio_out, ca)) {
-				BUF_MEM *bio_buf;
-				BIO_get_mem_ptr(bio_out, &bio_buf);
+			if (PEM_write_bio_X509(bio_out, ca) && BIO_get_mem_ptr(bio_out, &bio_buf) > 0) {
 				ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
 				add_index_zval(zout, i, &zcert);
 			}
@@ -3262,11 +3252,10 @@ PHP_FUNCTION(openssl_pkcs7_read)
 			goto clean_exit;
 		}
 		for (i = 0; i < sk_X509_CRL_num(crls); i++) {
+			BUF_MEM *bio_buf;
 			X509_CRL* crl = sk_X509_CRL_value(crls, i);
 
-			if (PEM_write_bio_X509_CRL(bio_out, crl)) {
-				BUF_MEM *bio_buf;
-				BIO_get_mem_ptr(bio_out, &bio_buf);
+			if (PEM_write_bio_X509_CRL(bio_out, crl) && BIO_get_mem_ptr(bio_out, &bio_buf) > 0) {
 				ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
 				add_index_zval(zout, i, &zcert);
 			}
@@ -3919,11 +3908,10 @@ PHP_FUNCTION(openssl_cms_read)
 		}
 
 		for (i = 0; i < sk_X509_num(certs); i++) {
+			BUF_MEM *bio_buf;
 			X509* ca = sk_X509_value(certs, i);
 
-			if (PEM_write_bio_X509(bio_out, ca)) {
-				BUF_MEM *bio_buf;
-				BIO_get_mem_ptr(bio_out, &bio_buf);
+			if (PEM_write_bio_X509(bio_out, ca) && BIO_get_mem_ptr(bio_out, &bio_buf) > 0) {
 				ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
 				add_index_zval(zout, i, &zcert);
 			}
@@ -3939,11 +3927,10 @@ PHP_FUNCTION(openssl_cms_read)
 		}
 
 		for (i = 0; i < sk_X509_CRL_num(crls); i++) {
+			BUF_MEM *bio_buf;
 			X509_CRL* crl = sk_X509_CRL_value(crls, i);
 
-			if (PEM_write_bio_X509_CRL(bio_out, crl)) {
-				BUF_MEM *bio_buf;
-				BIO_get_mem_ptr(bio_out, &bio_buf);
+			if (PEM_write_bio_X509_CRL(bio_out, crl) && BIO_get_mem_ptr(bio_out, &bio_buf) > 0) {
 				ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length);
 				add_index_zval(zout, i, &zcert);
 			}