Commit 398b7dabfbd for php.net

commit 398b7dabfbd2e8f4f4ed2065dbcf3e3794e8ca47
Merge: 411c0de215f a38418777f6
Author: Ilija Tovilo <ilija.tovilo@me.com>
Date:   Wed May 6 13:08:17 2026 +0200

    GHSA-m8rr-4c36-8gq4: Consistently pass unsigned char to ctype.h functions

    Fixes GHSA-m8rr-4c36-8gq4
    Fixes CVE-2026-7258

diff --cc Zend/zend_ini.c
index 2ab476e25bd,3201eec0492..6886ea702a1
--- a/Zend/zend_ini.c
+++ b/Zend/zend_ini.c
@@@ -595,10 -555,10 +595,10 @@@ static const char *zend_ini_consume_qua
  		++digits_consumed;
  	}

- 	if (digits_consumed[0] == '0' && !isdigit(digits_consumed[1])) {
+ 	if (digits_consumed[0] == '0' && !isdigit((unsigned char)digits_consumed[1])) {
  		/* Value is just 0 */
  		if ((digits_consumed+1) == str_end) {
 -			return digits;
 +			return digits_consumed;
  		}

  		switch (digits_consumed[1]) {
diff --cc Zend/zend_virtual_cwd.c
index 6bff2ad984d,a2ea4de165f..9975278ba2c
--- a/Zend/zend_virtual_cwd.c
+++ b/Zend/zend_virtual_cwd.c
@@@ -1129,11 -1129,8 +1129,11 @@@ CWD_API int virtual_file_ex(cwd_state *
  		resolved_path[start++] = DEFAULT_SLASH;
  	} else if (IS_ABSOLUTE_PATH(resolved_path, path_length)) {
  		/* skip DRIVE name */
- 		resolved_path[0] = toupper(resolved_path[0]);
+ 		resolved_path[0] = toupper((unsigned char)resolved_path[0]);
  		resolved_path[2] = DEFAULT_SLASH;
 +		if (path_length == 2) {
 +			resolved_path[3] = '\0';
 +		}
  		start = 3;
  	}
  #endif
diff --cc ext/standard/formatted_print.c
index 1ff0f36212b,a1927b5fe88..b8847d1801b
--- a/ext/standard/formatted_print.c
+++ b/ext/standard/formatted_print.c
@@@ -468,9 -464,9 +468,9 @@@ php_formatted_print(char *format, size_
  			always_sign = 0;
  			expprec = 0;

 -			PRINTF_DEBUG(("sprintf: first looking at '%c', inpos=%d\n",
 -						  *format, format - Z_STRVAL_P(z_format)));
 +			PRINTF_DEBUG(("sprintf: first looking at '%c', inpos=%zu\n",
 +						  *format, format - format_orig));
- 			if (isalpha((int)*format)) {
+ 			if (isalpha((unsigned char)*format)) {
  				width = precision = 0;
  				argnum = ARG_NUM_NEXT;
  			} else {
@@@ -539,10 -535,10 +539,10 @@@
  					}
  					width = Z_LVAL_P(tmp);
  					adjusting |= ADJ_WIDTH;
- 				} else if (isdigit((int)*format)) {
+ 				} else if (isdigit((unsigned char)*format)) {
  					PRINTF_DEBUG(("sprintf: getting width\n"));
  					if ((width = php_sprintf_getnumber(&format, &format_len)) < 0) {
 -						zend_value_error("Width must be greater than zero and less than %d", INT_MAX);
 +						zend_value_error("Width must be between 0 and %d", INT_MAX);
  						goto fail;
  					}
  					adjusting |= ADJ_WIDTH;
@@@ -584,9 -580,9 +584,9 @@@
  						precision = Z_LVAL_P(tmp);
  						adjusting |= ADJ_PRECISION;
  						expprec = 1;
- 					} else if (isdigit((int)*format)) {
+ 					} else if (isdigit((unsigned char)*format)) {
  						if ((precision = php_sprintf_getnumber(&format, &format_len)) < 0) {
 -							zend_value_error("Precision must be greater than zero and less than %d", INT_MAX);
 +							zend_value_error("Precision must be between 0 and %d", INT_MAX);
  							goto fail;
  						}
  						adjusting |= ADJ_PRECISION;
diff --cc ext/standard/metaphone.c
index 9bddc996690,01ae1f965c1..887c4b1702f
--- a/ext/standard/metaphone.c
+++ b/ext/standard/metaphone.c
@@@ -78,29 -78,21 +78,29 @@@ static const char _codes[26]
  };


 -#define ENCODE(c) (isalpha((unsigned char)(c)) ? _codes[((toupper((unsigned char)(c))) - 'A')] : 0)
 +/* Note: these functions require an uppercase letter input! */
 +static zend_always_inline char encode(char c) {
- 	if (isalpha(c)) {
++	if (isalpha((unsigned char)c)) {
 +		ZEND_ASSERT(c >= 'A' && c <= 'Z');
 +		return _codes[(c - 'A')];
 +	} else {
 +		return 0;
 +	}
 +}

 -#define isvowel(c)  (ENCODE(c) & 1)		/* AEIOU */
 +#define isvowel(c)  (encode(c) & 1)		/* AEIOU */

  /* These letters are passed through unchanged */
 -#define NOCHANGE(c) (ENCODE(c) & 2)		/* FJMNR */
 +#define NOCHANGE(c) (encode(c) & 2)		/* FJMNR */

  /* These form diphthongs when preceding H */
 -#define AFFECTH(c)  (ENCODE(c) & 4)		/* CGPST */
 +#define AFFECTH(c)  (encode(c) & 4)		/* CGPST */

  /* These make C and G soft */
 -#define MAKESOFT(c) (ENCODE(c) & 8)		/* EIY */
 +#define MAKESOFT(c) (encode(c) & 8)		/* EIY */

  /* These prevent GH from becoming F */
 -#define NOGHTOF(c)  (ENCODE(c) & 16)	/* BDH */
 +#define NOGHTOF(c)  (encode(c) & 16)	/* BDH */

  /*----------------------------- */
  /* end of "metachar.h"          */
@@@ -109,21 -101,18 +109,21 @@@
  /* I suppose I could have been using a character pointer instead of
   * accesssing the array directly... */

- #define Convert_Raw(c) toupper(c)
++#define Convert_Raw(c) toupper((unsigned char)c)
  /* Look at the next letter in the word */
 -#define Next_Letter (toupper((unsigned char)word[w_idx+1]))
 +#define Read_Raw_Next_Letter (word[w_idx+1])
 +#define Read_Next_Letter (Convert_Raw(Read_Raw_Next_Letter))
  /* Look at the current letter in the word */
 -#define Curr_Letter (toupper((unsigned char)word[w_idx]))
 +#define Read_Raw_Curr_Letter (word[w_idx])
 +#define Read_Curr_Letter (Convert_Raw(Read_Raw_Curr_Letter))
  /* Go N letters back. */
 -#define Look_Back_Letter(n)	(w_idx >= n ? toupper((unsigned char)word[w_idx-n]) : '\0')
 +#define Look_Back_Letter(n)	(w_idx >= n ? Convert_Raw(word[w_idx-n]) : '\0')
  /* Previous letter.  I dunno, should this return null on failure? */
 -#define Prev_Letter (Look_Back_Letter(1))
 +#define Read_Prev_Letter (Look_Back_Letter(1))
  /* Look two letters down.  It makes sure you don't walk off the string. */
 -#define After_Next_Letter	(Next_Letter != '\0' ? toupper((unsigned char)word[w_idx+2]) \
 +#define Read_After_Next_Letter	(Read_Raw_Next_Letter != '\0' ? Convert_Raw(word[w_idx+2]) \
  											     : '\0')
- #define Look_Ahead_Letter(n) (toupper(Lookahead((char *) word+w_idx, n)))
+ #define Look_Ahead_Letter(n) (toupper((unsigned char)Lookahead((char *) word+w_idx, n)))


  /* Allows us to safely look ahead an arbitrary # of letters */
@@@ -189,9 -179,9 +189,9 @@@ static void metaphone(unsigned char *wo

  /*-- The first phoneme has to be processed specially. --*/
  	/* Find our first letter */
- 	for (; !isalpha(curr_letter = Read_Raw_Curr_Letter); w_idx++) {
 -	for (; !isalpha((unsigned char)Curr_Letter); w_idx++) {
++	for (; !isalpha((unsigned char)(curr_letter = Read_Raw_Curr_Letter)); w_idx++) {
  		/* On the off chance we were given nothing but crap... */
 -		if (Curr_Letter == '\0') {
 +		if (curr_letter == '\0') {
  			End_Phoned_Word();
  			return;
  		}
@@@ -277,23 -263,18 +277,23 @@@
  		 */

  		/* Ignore non-alphas */
- 		if (!isalpha(curr_letter))
 -		if (!isalpha((unsigned char)Curr_Letter))
++		if (!isalpha((unsigned char)curr_letter))
  			continue;

 +		curr_letter = Convert_Raw(curr_letter);
 +		/* Note: we can't cache curr_letter from the previous loop
 +		 * because of the skip_letter variable. */
 +		char prev_letter = Read_Prev_Letter;
 +
  		/* Drop duplicates, except CC */
 -		if (Curr_Letter == Prev_Letter &&
 -			Curr_Letter != 'C')
 +		if (curr_letter == prev_letter &&
 +			curr_letter != 'C')
  			continue;

 -		switch (Curr_Letter) {
 +		switch (curr_letter) {
  			/* B -> B unless in MB */
  		case 'B':
 -			if (Prev_Letter != 'M')
 +			if (prev_letter != 'M')
  				Phonize('B');
  			break;
  			/* 'sh' if -CIA- or -CH, but not SCH, except SCHW.
diff --cc main/streams/transports.c
index 38850a3b541,0db8f4d0130..44f4050fec8
--- a/main/streams/transports.c
+++ b/main/streams/transports.c
@@@ -94,8 -94,7 +94,8 @@@ PHPAPI php_stream *_php_stream_xport_cr
  		}
  	}

 +	orig_path = name;
- 	for (p = name; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++) {
+ 	for (p = name; isalnum((unsigned char)*p) || *p == '+' || *p == '-' || *p == '.'; p++) {
  		n++;
  	}

diff --cc sapi/phpdbg/phpdbg_utils.c
index 964d82ef6c8,04bc117ffb3..fad8c9dfb76
--- a/sapi/phpdbg/phpdbg_utils.c
+++ b/sapi/phpdbg/phpdbg_utils.c
@@@ -199,7 -199,7 +199,7 @@@ PHPDBG_API char *phpdbg_trim(const cha
  	const char *p = str;
  	char *new = NULL;

- 	while (isspace(*p)) {
 -	while (p && isspace((unsigned char)*p)) {
++	while (isspace((unsigned char)*p)) {
  		++p;
  		--len;
  	}