File php7-CVE-2024-8929.patch of Package php7.37962

Index: php-7.4.33/ext/mysqlnd/mysqlnd_ps_codec.c
===================================================================
--- php-7.4.33.orig/ext/mysqlnd/mysqlnd_ps_codec.c
+++ php-7.4.33/ext/mysqlnd/mysqlnd_ps_codec.c
@@ -52,11 +52,45 @@ struct st_mysqlnd_perm_bind mysqlnd_ps_f
 #define MYSQLND_PS_SKIP_RESULT_W_LEN	-1
 #define MYSQLND_PS_SKIP_RESULT_STR		-2
 
+static inline void ps_fetch_over_read_error(const zend_uchar ** row)
+{
+	php_error_docref(NULL, E_WARNING, "Malformed server packet. Field length pointing after the end of packet");
+	*row = NULL;
+}
+
+static inline int ps_fetch_is_packet_over_read_with_variable_length(const unsigned int pack_len,
+		const zend_uchar ** row, const zend_uchar *p, unsigned int length)
+{
+	if (pack_len == 0) {
+		return 0;
+	}
+	size_t length_len = *row - p;
+	if (length_len > pack_len || length > pack_len - length_len) {
+		ps_fetch_over_read_error(row);
+		return 1;
+	}
+	return 0;
+}
+
+static inline int ps_fetch_is_packet_over_read_with_static_length(const unsigned int pack_len,
+		const zend_uchar ** row, unsigned int length)
+{
+	if (pack_len > 0 && length > pack_len) {
+		ps_fetch_over_read_error(row);
+		return 1;
+	}
+	return 0;
+}
+
 /* {{{ ps_fetch_from_1_to_8_bytes */
 void
 ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len,
 						   const zend_uchar ** row, unsigned int byte_count)
 {
+	if (UNEXPECTED(ps_fetch_is_packet_over_read_with_static_length(pack_len, row, byte_count))) {
+		return;
+	}
+
 	char tmp[22];
 	size_t tmp_len = 0;
 	zend_bool is_bit = field->type == MYSQL_TYPE_BIT;
@@ -178,6 +212,11 @@ ps_fetch_float(zval * zv, const MYSQLND_
 	float fval;
 	double dval;
 	DBG_ENTER("ps_fetch_float");
+
+	if (UNEXPECTED(ps_fetch_is_packet_over_read_with_static_length(pack_len, row, 4))) {
+		return;
+	}
+
 	float4get(fval, *row);
 	(*row)+= 4;
 	DBG_INF_FMT("value=%f", fval);
@@ -200,6 +239,11 @@ ps_fetch_double(zval * zv, const MYSQLND
 {
 	double value;
 	DBG_ENTER("ps_fetch_double");
+
+	if (UNEXPECTED(ps_fetch_is_packet_over_read_with_static_length(pack_len, row, 8))) {
+		return;
+	}
+
 	float8get(value, *row);
 	ZVAL_DOUBLE(zv, value);
 	(*row)+= 8;
@@ -216,9 +260,14 @@ ps_fetch_time(zval * zv, const MYSQLND_F
 	struct st_mysqlnd_time t;
 	zend_ulong length; /* First byte encodes the length*/
 	char * value;
+        const zend_uchar *p = *row;
 	DBG_ENTER("ps_fetch_time");
 
 	if ((length = php_mysqlnd_net_field_length(row))) {
+		if (UNEXPECTED(ps_fetch_is_packet_over_read_with_variable_length(pack_len, row, p, length))) {
+			return;
+		}
+
 		const zend_uchar * to = *row;
 
 		t.time_type = MYSQLND_TIMESTAMP_TIME;
@@ -273,9 +322,14 @@ ps_fetch_date(zval * zv, const MYSQLND_F
 	struct st_mysqlnd_time t = {0};
 	zend_ulong length; /* First byte encodes the length*/
 	char * value;
+	const zend_uchar *p = *row;
 	DBG_ENTER("ps_fetch_date");
 
 	if ((length = php_mysqlnd_net_field_length(row))) {
+		if (UNEXPECTED(ps_fetch_is_packet_over_read_with_variable_length(pack_len, row, p, length))) {
+			return;
+		}
+
 		const zend_uchar * to = *row;
 
 		t.time_type = MYSQLND_TIMESTAMP_DATE;
@@ -310,9 +364,14 @@ ps_fetch_datetime(zval * zv, const MYSQL
 	struct st_mysqlnd_time t;
 	zend_ulong length; /* First byte encodes the length*/
 	char * value;
+	const zend_uchar *p = *row;
 	DBG_ENTER("ps_fetch_datetime");
 
 	if ((length = php_mysqlnd_net_field_length(row))) {
+		if (UNEXPECTED(ps_fetch_is_packet_over_read_with_variable_length(pack_len, row, p, length))) {
+			return;
+		}
+
 		const zend_uchar * to = *row;
 
 		t.time_type = MYSQLND_TIMESTAMP_DATETIME;
@@ -371,7 +430,11 @@ ps_fetch_string(zval * zv, const MYSQLND
 	  For now just copy, before we make it possible
 	  to write \0 to the row buffer
 	*/
+	const zend_uchar *p = *row;
 	const zend_ulong length = php_mysqlnd_net_field_length(row);
+	if (UNEXPECTED(ps_fetch_is_packet_over_read_with_variable_length(pack_len, row, p, length))) {
+		return;
+	}
 	DBG_ENTER("ps_fetch_string");
 	DBG_INF_FMT("len = %lu", length);
 	DBG_INF("copying from the row buffer");
@@ -387,7 +450,11 @@ ps_fetch_string(zval * zv, const MYSQLND
 static void
 ps_fetch_bit(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row)
 {
+	const zend_uchar *p = *row;
 	const zend_ulong length = php_mysqlnd_net_field_length(row);
+	if (UNEXPECTED(ps_fetch_is_packet_over_read_with_variable_length(pack_len, row, p, length))) {
+		return;
+	}
 	ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, length);
 }
 /* }}} */
Index: php-7.4.33/ext/mysqlnd/mysqlnd_result.c
===================================================================
--- php-7.4.33.orig/ext/mysqlnd/mysqlnd_result.c
+++ php-7.4.33/ext/mysqlnd/mysqlnd_result.c
@@ -505,7 +505,7 @@ mysqlnd_query_read_result_set_header(MYS
 				if (FAIL == (ret = result->m.read_result_metadata(result, conn))) {
 					/* For PS, we leave them in Prepared state */
 					if (!stmt && conn->current_result) {
-						mnd_efree(conn->current_result);
+						conn->current_result->m.free_result(conn->current_result, TRUE);
 						conn->current_result = NULL;
 					}
 					DBG_ERR("Error occurred while reading metadata");
Index: php-7.4.33/ext/mysqlnd/mysqlnd_wireprotocol.c
===================================================================
--- php-7.4.33.orig/ext/mysqlnd/mysqlnd_wireprotocol.c
+++ php-7.4.33/ext/mysqlnd/mysqlnd_wireprotocol.c
@@ -715,7 +715,14 @@ php_mysqlnd_auth_response_read(MYSQLND_C
 
 		/* There is a message */
 		if (packet->header.size > (size_t) (p - buf) && (net_len = php_mysqlnd_net_field_length(&p))) {
-			packet->message_len = MIN(net_len, buf_len - (p - begin));
+			/* p can get past packet size when getting field length so it needs to be checked first
+			 * and after that it can be checked that the net_len is not greater than the packet size */
+			if ((p - buf) > packet->header.size || packet->header.size - (p - buf) < net_len) {
+				DBG_ERR_FMT("OK packet message length is past the packet size");
+				php_error_docref(NULL, E_WARNING, "OK packet message length is past the packet size");
+				DBG_RETURN(FAIL);
+			}
+			packet->message_len = net_len;
 			packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE);
 		} else {
 			packet->message = NULL;
@@ -1113,6 +1120,17 @@ php_mysqlnd_rset_header_read(MYSQLND_CON
 			BAIL_IF_NO_MORE_DATA;
 			/* Check for additional textual data */
 			if (packet->header.size  > (size_t) (p - buf) && (len = php_mysqlnd_net_field_length(&p))) {
+				/* p can get past packet size when getting field length so it needs to be checked first
+				 * and after that it can be checked that the len is not greater than the packet size */
+				if ((p - buf) > packet->header.size || packet->header.size - (p - buf) < len) {
+					size_t local_file_name_over_read = ((p - buf) - packet->header.size) + len;
+					DBG_ERR_FMT("RSET_HEADER packet additional data length is past %zu bytes the packet size",
+						local_file_name_over_read);
+					php_error_docref(NULL, E_WARNING,
+						"RSET_HEADER packet additional data length is past %zu bytes the packet size",
+						local_file_name_over_read);
+					DBG_RETURN(FAIL);
+				}
 				packet->info_or_local_file.s = mnd_emalloc(len + 1);
 				if (packet->info_or_local_file.s) {
 					memcpy(packet->info_or_local_file.s, p, len);
@@ -1272,22 +1290,16 @@ php_mysqlnd_rset_field_read(MYSQLND_CONN
 	}
 
 
-	/*
-	  def could be empty, thus don't allocate on the root.
-	  NULL_LENGTH (0xFB) comes from COM_FIELD_LIST when the default value is NULL.
-	  Otherwise the string is length encoded.
-	*/
+	/* COM_FIELD_LIST is no longer supported so def should not be present */
 	if (packet->header.size > (size_t) (p - buf) &&
 		(len = php_mysqlnd_net_field_length(&p)) &&
 		len != MYSQLND_NULL_LENGTH)
 	{
-		BAIL_IF_NO_MORE_DATA;
-		DBG_INF_FMT("Def found, length %lu", len);
-		meta->def = packet->memory_pool->get_chunk(packet->memory_pool, len + 1);
-		memcpy(meta->def, p, len);
-		meta->def[len] = '\0';
-		meta->def_length = len;
-		p += len;
+		DBG_ERR_FMT("Protocol error. Server sent default for unsupported field list");
+		php_error_docref(NULL, E_WARNING,
+			"Protocol error. Server sent default for unsupported field list (mysqlnd_wireprotocol.c:%u)",
+			__LINE__);
+		DBG_RETURN(FAIL);
 	}
 
 	root_ptr = meta->root = packet->memory_pool->get_chunk(packet->memory_pool, total_len);
@@ -1462,8 +1474,10 @@ php_mysqlnd_rowp_read_binary_protocol(MY
 									  const unsigned int field_count, const MYSQLND_FIELD * const fields_metadata,
 									  const zend_bool as_int_or_float, MYSQLND_STATS * const stats)
 {
-	unsigned int i;
-	const zend_uchar * p = row_buffer->ptr;
+	unsigned int i, j;
+	size_t rbs = row_buffer->size;
+	const zend_uchar * rbp = row_buffer->ptr;
+	const zend_uchar * p = rbp;
 	const zend_uchar * null_ptr;
 	zend_uchar bit;
 	zval *current_field, *end_field, *start_field;
@@ -1496,7 +1510,21 @@ php_mysqlnd_rowp_read_binary_protocol(MY
 			statistic = STAT_BINARY_TYPE_FETCHED_NULL;
 		} else {
 			enum_mysqlnd_field_types type = fields_metadata[i].type;
-			mysqlnd_ps_fetch_functions[type].func(current_field, &fields_metadata[i], 0, &p);
+			size_t row_position = p - rbp;
+			if (rbs <= row_position) {
+				for (j = 0, current_field = start_field; j < i; current_field++, j++) {
+					zval_ptr_dtor(current_field);
+				}
+				php_error_docref(NULL, E_WARNING, "Malformed server packet. No packet space left for the field");
+				DBG_RETURN(FAIL);
+			}
+			mysqlnd_ps_fetch_functions[type].func(current_field, &fields_metadata[i], rbs - row_position, &p);
+			if (p == NULL) {
+				for (j = 0, current_field = start_field; j < i; current_field++, j++) {
+					zval_ptr_dtor(current_field);
+				}
+				DBG_RETURN(FAIL);
+			}
 
 			if (MYSQLND_G(collect_statistics)) {
 				switch (fields_metadata[i].type) {
@@ -1553,7 +1581,7 @@ php_mysqlnd_rowp_read_text_protocol_aux(
 									unsigned int field_count, const MYSQLND_FIELD * fields_metadata,
 									zend_bool as_int_or_float, MYSQLND_STATS * stats)
 {
-	unsigned int i;
+	unsigned int i, j;
 	zval *current_field, *end_field, *start_field;
 	zend_uchar * p = row_buffer->ptr;
 	const size_t data_size = row_buffer->size;
@@ -1574,9 +1602,11 @@ php_mysqlnd_rowp_read_text_protocol_aux(
 		/* NULL or NOT NULL, this is the question! */
 		if (len == MYSQLND_NULL_LENGTH) {
 			ZVAL_NULL(current_field);
-		} else if ((p + len) > packet_end) {
-			php_error_docref(NULL, E_WARNING, "Malformed server packet. Field length pointing "MYSQLND_SZ_T_SPEC
-											  " bytes after end of packet", (p + len) - packet_end - 1);
+		} else if (p > packet_end || len > packet_end - p) {
+			php_error_docref(NULL, E_WARNING, "Malformed server packet. Field length pointing after end of packet");
+			for (j = 0, current_field = start_field; j < i; current_field++, j++) {
+				zval_ptr_dtor(current_field);
+			}
 			DBG_RETURN(FAIL);
 		} else {
 #if defined(MYSQLND_STRING_TO_INT_CONVERSION)
openSUSE Build Service is sponsored by