Skip to content

Commit 68be6fe

Browse files
committed
Fix GH-21738: undefined behavior in url_decode with non-ASCII bytes
The isxdigit() family requires its argument to be representable as unsigned char (0-255) or EOF. Casting a signed char value holding a high-bit byte (e.g. 0x80) to int produces a negative number (-128) which triggers undefined behavior, and on some libc implementations (e.g. NetBSD) can lead to out-of-bounds reads through the internal character classification table.
1 parent 1462499 commit 68be6fe

2 files changed

Lines changed: 6 additions & 4 deletions

File tree

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ PHP NEWS
162162
. Fix NUL byte truncation in sqlite3 TEXT column handling. (ndossche)
163163

164164
- Standard:
165+
. Fixed bug GH-21738 (undefined behavior in url_decode functions when
166+
passing non-ASCII bytes to isxdigit()). (lacatoire)
165167
. Fixed bug GH-19926 (reset internal pointer earlier while splicing array
166168
while COW violation flag is still set). (alexandre-daubois)
167169
. Added form feed (\f) in the default trimmed characters of trim(), rtrim()

ext/standard/url.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -589,8 +589,8 @@ PHPAPI size_t php_url_decode_ex(char *dest, const char *src, size_t src_len)
589589
if (*data == '+') {
590590
*dest = ' ';
591591
}
592-
else if (*data == '%' && src_len >= 2 && isxdigit((int) *(data + 1))
593-
&& isxdigit((int) *(data + 2))) {
592+
else if (*data == '%' && src_len >= 2 && isxdigit((unsigned char) *(data + 1))
593+
&& isxdigit((unsigned char) *(data + 2))) {
594594
*dest = (char) php_htoi(data + 1);
595595
data += 2;
596596
src_len -= 2;
@@ -662,8 +662,8 @@ PHPAPI size_t php_raw_url_decode_ex(char *dest, const char *src, size_t src_len)
662662
const char *data = src;
663663

664664
while (src_len--) {
665-
if (*data == '%' && src_len >= 2 && isxdigit((int) *(data + 1))
666-
&& isxdigit((int) *(data + 2))) {
665+
if (*data == '%' && src_len >= 2 && isxdigit((unsigned char) *(data + 1))
666+
&& isxdigit((unsigned char) *(data + 2))) {
667667
*dest = (char) php_htoi(data + 1);
668668
data += 2;
669669
src_len -= 2;

0 commit comments

Comments
 (0)