Commit b8dad9314c1 for php.net

commit b8dad9314c1e225a1a2d50608e4e7d478c34365c
Merge: 3f6f38bc12b dc9e21b81c1
Author: Ilija Tovilo <ilija.tovilo@me.com>
Date:   Wed May 6 13:10:12 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 ext/filter/logical_filters.c
index 2dec2361141,634496027c8..017ac41a65a
--- a/ext/filter/logical_filters.c
+++ b/ext/filter/logical_filters.c
@@@ -535,22 -528,22 +535,22 @@@ static bool php_filter_validate_domain_
  	}

  	/* First char must be alphanumeric */
- 	if(*s == '.' || (hostname && !isalnum((int)*(unsigned char *)s))) {
+ 	if(*s == '.' || (hostname && !isalnum((unsigned char)*s))) {
 -		return 0;
 +		return false;
  	}

  	while (s < e) {
  		if (*s == '.') {
  			/* The first and the last character of a label must be alphanumeric */
- 			if (*(s + 1) == '.' || (hostname && (!isalnum((int)*(unsigned char *)(s - 1)) || !isalnum((int)*(unsigned char *)(s + 1))))) {
+ 			if (*(s + 1) == '.' || (hostname && (!isalnum((unsigned char)s[-1]) || !isalnum((unsigned char)s[1])))) {
 -				return 0;
 +				return false;
  			}

  			/* Reset label length counter */
  			i = 1;
  		} else {
- 			if (i > 63 || (hostname && (*s != '-' || *(s + 1) == '\0') && !isalnum((int)*(unsigned char *)s))) {
+ 			if (i > 63 || (hostname && (*s != '-' || *(s + 1) == '\0') && !isalnum((unsigned char)*s))) {
 -				return 0;
 +				return false;
  			}

  			i++;
@@@ -572,32 -564,33 +572,32 @@@ zend_result php_filter_validate_domain(
  }
  /* }}} */

 -static int is_userinfo_valid(zend_string *str)
 +static bool is_userinfo_valid(const zend_string *str)
  {
 -	const char *valid = "-._~!$&'()*+,;=:";
  	const char *p = ZSTR_VAL(str);
  	while (p - ZSTR_VAL(str) < ZSTR_LEN(str)) {
 +		static const char *valid = "-._~!$&'()*+,;=:";
- 		if (isalpha(*p) || isdigit(*p) || strchr(valid, *p)) {
+ 		if (isalpha((unsigned char)*p) || isdigit((unsigned char)*p) || strchr(valid, *p)) {
  			p++;
- 		} else if (*p == '%' && p - ZSTR_VAL(str) <= ZSTR_LEN(str) - 3 && isdigit(*(p+1)) && isxdigit(*(p+2))) {
+ 		} else if (*p == '%' && p - ZSTR_VAL(str) <= ZSTR_LEN(str) - 3 && isdigit((unsigned char)p[1]) && isxdigit((unsigned char)p[2])) {
  			p += 3;
  		} else {
 -			return 0;
 +			return false;
  		}
  	}
 -	return 1;
 +	return true;
  }

 -static bool php_filter_is_valid_ipv6_hostname(const char *s, size_t l)
 +static bool php_filter_is_valid_ipv6_hostname(const zend_string *s)
  {
 -	const char *e = s + l;
 +	const char *e = ZSTR_VAL(s) + ZSTR_LEN(s);
  	const char *t = e - 1;

 -	return *s == '[' && *t == ']' && _php_filter_validate_ipv6(s + 1, l - 2, NULL);
 +	return *ZSTR_VAL(s) == '[' && *t == ']' && _php_filter_validate_ipv6(ZSTR_VAL(s) + 1, ZSTR_LEN(s) - 2, NULL);
  }

 -void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
 +zend_result php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
  {
 -	php_url *url;
  	size_t old_len = Z_STRLEN_P(value);

  	php_filter_url(value, flags, option_array, charset);
diff --cc ext/ftp/ftp.c
index 5291a1f172d,36a0d1697ec..a6afed74859
--- a/ext/ftp/ftp.c
+++ b/ext/ftp/ftp.c
@@@ -785,16 -855,16 +785,16 @@@ bool ftp_pasv(ftpbuf_t *ftp, int pasv
  #endif

  	if (!ftp_putcmd(ftp, "PASV",  sizeof("PASV")-1, NULL, (size_t) 0)) {
 -		return 0;
 +		return false;
  	}
  	if (!ftp_getresp(ftp) || ftp->resp != 227) {
 -		return 0;
 +		return false;
  	}
  	/* parse out the IP and port */
- 	for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
+ 	for (ptr = ftp->inbuf; *ptr && !isdigit((unsigned char)*ptr); ptr++);
  	n = sscanf(ptr, "%lu,%lu,%lu,%lu,%lu,%lu", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]);
  	if (n != 6) {
 -		return 0;
 +		return false;
  	}
  	for (n = 0; n < 6; n++) {
  		ipbox.c[n] = (unsigned char) b[n];
@@@ -1284,8 -1392,8 +1284,8 @@@ static bool ftp_getresp(ftpbuf_t *ftp
  	}

  	/* translate the tag */
- 	if (!isdigit(ftp->inbuf[0]) || !isdigit(ftp->inbuf[1]) || !isdigit(ftp->inbuf[2])) {
+ 	if (!isdigit((unsigned char)ftp->inbuf[0]) || !isdigit((unsigned char)ftp->inbuf[1]) || !isdigit((unsigned char)ftp->inbuf[2])) {
 -		return 0;
 +		return false;
  	}

  	ftp->resp = 100 * (ftp->inbuf[0] - '0') + 10 * (ftp->inbuf[1] - '0') + (ftp->inbuf[2] - '0');
diff --cc ext/standard/url.c
index 504805484ef,c47e21af71b..7bfaa719a84
--- a/ext/standard/url.c
+++ b/ext/standard/url.c
@@@ -591,11 -588,11 +591,11 @@@ PHPAPI size_t php_url_decode_ex(char *d
  		if (*data == '+') {
  			*dest = ' ';
  		}
- 		else if (*data == '%' && src_len >= 2 && isxdigit((int) *(data + 1))
- 				 && isxdigit((int) *(data + 2))) {
 -		else if (*data == '%' && len >= 2 && isxdigit((unsigned char)data[1])
++		else if (*data == '%' && src_len >= 2 && isxdigit((unsigned char)data[1])
+ 				 && isxdigit((unsigned char)data[2])) {
  			*dest = (char) php_htoi(data + 1);
  			data += 2;
 -			len -= 2;
 +			src_len -= 2;
  		} else {
  			*dest = *data;
  		}
@@@ -658,17 -640,18 +658,17 @@@ PHP_FUNCTION(rawurldecode
  }
  /* }}} */

 -/* {{{ php_raw_url_decode */
 -PHPAPI size_t php_raw_url_decode(char *str, size_t len)
 +PHPAPI size_t php_raw_url_decode_ex(char *dest, const char *src, size_t src_len)
  {
 -	char *dest = str;
 -	char *data = str;
 +	char *dest_start = dest;
 +	const char *data = src;

 -	while (len--) {
 -		if (*data == '%' && len >= 2 && isxdigit((unsigned char)data[1])
 +	while (src_len--) {
- 		if (*data == '%' && src_len >= 2 && isxdigit((int) *(data + 1))
- 			&& isxdigit((int) *(data + 2))) {
++		if (*data == '%' && src_len >= 2 && isxdigit((unsigned char)data[1])
+ 			&& isxdigit((unsigned char)data[2])) {
  			*dest = (char) php_htoi(data + 1);
  			data += 2;
 -			len -= 2;
 +			src_len -= 2;
  		} else {
  			*dest = *data;
  		}
diff --cc main/streams/streams.c
index 85d2947c28a,6feb8b7c01b..73fdc785721
--- a/main/streams/streams.c
+++ b/main/streams/streams.c
@@@ -1918,10 -1918,12 +1918,10 @@@ void php_shutdown_stream_wrappers(int m
  /* Validate protocol scheme names during registration
   * Must conform to /^[a-zA-Z0-9+.-]+$/
   */
 -static inline zend_result php_stream_wrapper_scheme_validate(const char *protocol, unsigned int protocol_len)
 +static inline zend_result php_stream_wrapper_scheme_validate(const char *protocol, size_t protocol_len)
  {
 -	unsigned int i;
 -
 -	for(i = 0; i < protocol_len; i++) {
 +	for (size_t i = 0; i < protocol_len; i++) {
- 		if (!isalnum((int)protocol[i]) &&
+ 		if (!isalnum((unsigned char)protocol[i]) &&
  			protocol[i] != '+' &&
  			protocol[i] != '-' &&
  			protocol[i] != '.') {