Commit 284e202d253 for php.net

commit 284e202d2535199fd096c56c699136a0134718da
Author: Tim Düsterhus <tim@tideways-gmbh.com>
Date:   Mon Dec 15 16:19:32 2025 +0100

    uri: Update to uriparser-0.9.9-85-g9a31011 (#20707)

    This is specifically to import uriparser/uriparser#284 to fix CVE-2025-67899.

diff --git a/NEWS b/NEWS
index a09a5dfda79..3dd85860a2e 100644
--- a/NEWS
+++ b/NEWS
@@ -107,6 +107,12 @@ PHP                                                                        NEWS
   . Fixed bug GH-20370 (User stream filters could violate typed property
     constraints). (alexandre-daubois)

+- URI:
+  . Fixed bug GH-20366 (ext/uri incorrectly throws ValueError when encountering
+    null byte). (kocsismate)
+  . Fixed CVE-2025-67899 (uriparser through 0.9.9 allows unbounded recursion
+    and stack consumption). (Sebastian Pipping)
+
 - XML:
   . Fixed bug GH-20439 (xml_set_default_handler() does not properly handle
     special characters in attributes when passing data to callback). (ndossche)
@@ -119,10 +125,6 @@ PHP                                                                        NEWS
   . Fix assertion failures resulting in crashes with stream filter
     object parameters. (ndossche)

-- URI:
-  . Fixed bug GH-20366 (ext/uri incorrectly throws ValueError when encountering
-    null byte). (kocsismate)
-
 20 Nov 2025, PHP 8.5.0

 - Core:
diff --git a/ext/uri/uriparser/src/UriParse.c b/ext/uri/uriparser/src/UriParse.c
index ed851e94fbd..cada02301e0 100644
--- a/ext/uri/uriparser/src/UriParse.c
+++ b/ext/uri/uriparser/src/UriParse.c
@@ -81,8 +81,7 @@ static const URI_CHAR * URI_FUNC(ParseAuthority)(URI_TYPE(ParserState) * state,
 static const URI_CHAR * URI_FUNC(ParseAuthorityTwo)(URI_TYPE(ParserState) * state,
                                                     const URI_CHAR * first,
                                                     const URI_CHAR * afterLast);
-static const URI_CHAR * URI_FUNC(ParseHexZero)(URI_TYPE(ParserState) * state,
-                                               const URI_CHAR * first,
+static const URI_CHAR * URI_FUNC(ParseHexZero)(const URI_CHAR * first,
                                                const URI_CHAR * afterLast);
 static const URI_CHAR * URI_FUNC(ParseHierPart)(URI_TYPE(ParserState) * state,
                                                 const URI_CHAR * first,
@@ -92,10 +91,6 @@ static const URI_CHAR * URI_FUNC(ParseIpFutLoop)(URI_TYPE(ParserState) * state,
                                                  const URI_CHAR * first,
                                                  const URI_CHAR * afterLast,
                                                  UriMemoryManager * memory);
-static const URI_CHAR * URI_FUNC(ParseIpFutStopGo)(URI_TYPE(ParserState) * state,
-                                                   const URI_CHAR * first,
-                                                   const URI_CHAR * afterLast,
-                                                   UriMemoryManager * memory);
 static const URI_CHAR * URI_FUNC(ParseIpLit2)(URI_TYPE(ParserState) * state,
                                               const URI_CHAR * first,
                                               const URI_CHAR * afterLast,
@@ -116,10 +111,6 @@ static const URI_CHAR * URI_FUNC(ParseOwnHost2)(URI_TYPE(ParserState) * state,
                                                 const URI_CHAR * first,
                                                 const URI_CHAR * afterLast,
                                                 UriMemoryManager * memory);
-static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfo)(URI_TYPE(ParserState) * state,
-                                                       const URI_CHAR * first,
-                                                       const URI_CHAR * afterLast,
-                                                       UriMemoryManager * memory);
 static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfoNz)(URI_TYPE(ParserState) * state,
                                                          const URI_CHAR * first,
                                                          const URI_CHAR * afterLast,
@@ -160,8 +151,7 @@ static const URI_CHAR * URI_FUNC(ParsePctSubUnres)(URI_TYPE(ParserState) * state
                                                    const URI_CHAR * first,
                                                    const URI_CHAR * afterLast,
                                                    UriMemoryManager * memory);
-static const URI_CHAR * URI_FUNC(ParsePort)(URI_TYPE(ParserState) * state,
-                                            const URI_CHAR * first,
+static const URI_CHAR * URI_FUNC(ParsePort)(const URI_CHAR * first,
                                             const URI_CHAR * afterLast);
 static const URI_CHAR * URI_FUNC(ParseQueryFrag)(URI_TYPE(ParserState) * state,
                                                  const URI_CHAR * first,
@@ -292,8 +282,7 @@ URI_FUNC(ParseAuthorityTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * firs

     switch (*first) {
     case _UT(':'): {
-        const URI_CHAR * const afterPort =
-            URI_FUNC(ParsePort)(state, first + 1, afterLast);
+        const URI_CHAR * const afterPort = URI_FUNC(ParsePort)(first + 1, afterLast);
         if (afterPort == NULL) {
             return NULL;
         }
@@ -311,16 +300,17 @@ URI_FUNC(ParseAuthorityTwo)(URI_TYPE(ParserState) * state, const URI_CHAR * firs
  * [hexZero]->[HEXDIG][hexZero]
  * [hexZero]-><NULL>
  */
-static const URI_CHAR * URI_FUNC(ParseHexZero)(URI_TYPE(ParserState) * state,
-                                               const URI_CHAR * first,
+static const URI_CHAR * URI_FUNC(ParseHexZero)(const URI_CHAR * first,
                                                const URI_CHAR * afterLast) {
+tail_call:
     if (first >= afterLast) {
         return afterLast;
     }

     switch (*first) {
     case URI_SET_HEXDIG(_UT):
-        return URI_FUNC(ParseHexZero)(state, first + 1, afterLast);
+        first += 1;
+        goto tail_call;

     default:
         return first;
@@ -356,49 +346,37 @@ static URI_INLINE const URI_CHAR * URI_FUNC(ParseHierPart)(URI_TYPE(ParserState)
  * [ipFutLoop]->[subDelims][ipFutStopGo]
  * [ipFutLoop]->[unreserved][ipFutStopGo]
  * [ipFutLoop]-><:>[ipFutStopGo]
+ *
+ * [ipFutStopGo]->[ipFutLoop]
+ * [ipFutStopGo]-><NULL>
  */
 static const URI_CHAR * URI_FUNC(ParseIpFutLoop)(URI_TYPE(ParserState) * state,
                                                  const URI_CHAR * first,
                                                  const URI_CHAR * afterLast,
                                                  UriMemoryManager * memory) {
-    if (first >= afterLast) {
-        URI_FUNC(StopSyntax)(state, afterLast, memory);
-        return NULL;
-    }
+    const URI_CHAR * const originalFirst = first;

-    switch (*first) {
-    case _UT(':'):
-    case URI_SET_SUB_DELIMS(_UT):
-    case URI_SET_UNRESERVED(_UT):
-        return URI_FUNC(ParseIpFutStopGo)(state, first + 1, afterLast, memory);
+    while (first < afterLast) {
+        switch (*first) {
+        case _UT(':'):
+        case URI_SET_SUB_DELIMS(_UT):
+        case URI_SET_UNRESERVED(_UT):
+            first += 1;
+            break;

-    default:
-        URI_FUNC(StopSyntax)(state, first, memory);
-        return NULL;
+        default:
+            goto done_looping;
+            break;
+        }
     }
-}

-/*
- * [ipFutStopGo]->[ipFutLoop]
- * [ipFutStopGo]-><NULL>
- */
-static const URI_CHAR * URI_FUNC(ParseIpFutStopGo)(URI_TYPE(ParserState) * state,
-                                                   const URI_CHAR * first,
-                                                   const URI_CHAR * afterLast,
-                                                   UriMemoryManager * memory) {
-    if (first >= afterLast) {
-        return afterLast;
+done_looping:
+    if (first == originalFirst) {
+        URI_FUNC(StopSyntax)(state, first, memory);
+        return NULL;
     }

-    switch (*first) {
-    case _UT(':'):
-    case URI_SET_SUB_DELIMS(_UT):
-    case URI_SET_UNRESERVED(_UT):
-        return URI_FUNC(ParseIpFutLoop)(state, first, afterLast, memory);
-
-    default:
-        return first;
-    }
+    return first;
 }

 /*
@@ -430,7 +408,7 @@ static const URI_CHAR * URI_FUNC(ParseIpFuture)(URI_TYPE(ParserState) * state,
     case URI_SET_HEXDIG(_UT): {
         const URI_CHAR * afterIpFutLoop;
         const URI_CHAR * const afterHexZero =
-            URI_FUNC(ParseHexZero)(state, first + 2, afterLast);
+            URI_FUNC(ParseHexZero)(first + 2, afterLast);
         if (afterHexZero == NULL) {
             return NULL;
         }
@@ -832,6 +810,7 @@ static const URI_CHAR * URI_FUNC(ParseMustBeSegmentNzNc)(URI_TYPE(ParserState) *
                                                          const URI_CHAR * first,
                                                          const URI_CHAR * afterLast,
                                                          UriMemoryManager * memory) {
+tail_call:
     if (first >= afterLast) {
         if (!URI_FUNC(PushPathSegment)(state, state->uri->scheme.first, first,
                                        memory)) { /* SEGMENT BOTH */
@@ -849,14 +828,15 @@ static const URI_CHAR * URI_FUNC(ParseMustBeSegmentNzNc)(URI_TYPE(ParserState) *
         if (afterPctEncoded == NULL) {
             return NULL;
         }
-        return URI_FUNC(ParseMustBeSegmentNzNc)(state, afterPctEncoded, afterLast,
-                                                memory);
+        first = afterPctEncoded;
+        goto tail_call;
     }

     case _UT('@'):
     case URI_SET_SUB_DELIMS(_UT):
     case URI_SET_UNRESERVED(_UT):
-        return URI_FUNC(ParseMustBeSegmentNzNc)(state, first + 1, afterLast, memory);
+        first += 1;
+        goto tail_call;

     case _UT('/'): {
         const URI_CHAR * afterZeroMoreSlashSegs;
@@ -953,6 +933,7 @@ static const URI_CHAR * URI_FUNC(ParseOwnHost2)(URI_TYPE(ParserState) * state,
                                                 const URI_CHAR * first,
                                                 const URI_CHAR * afterLast,
                                                 UriMemoryManager * memory) {
+tail_call:
     if (first >= afterLast) {
         if (!URI_FUNC(OnExitOwnHost2)(state, first, memory)) {
             URI_FUNC(StopMalloc)(state, memory);
@@ -970,7 +951,8 @@ static const URI_CHAR * URI_FUNC(ParseOwnHost2)(URI_TYPE(ParserState) * state,
         if (afterPctSubUnres == NULL) {
             return NULL;
         }
-        return URI_FUNC(ParseOwnHost2)(state, afterPctSubUnres, afterLast, memory);
+        first = afterPctSubUnres;
+        goto tail_call;
     }

     default:
@@ -1006,74 +988,69 @@ static URI_INLINE UriBool URI_FUNC(OnExitOwnHostUserInfo)(URI_TYPE(ParserState)
     return URI_TRUE; /* Success */
 }

-/*
- * [ownHostUserInfo]->[ownHostUserInfoNz]
- * [ownHostUserInfo]-><NULL>
- */
-static URI_INLINE const URI_CHAR *
-URI_FUNC(ParseOwnHostUserInfo)(URI_TYPE(ParserState) * state, const URI_CHAR * first,
-                               const URI_CHAR * afterLast, UriMemoryManager * memory) {
-    if (first >= afterLast) {
-        if (!URI_FUNC(OnExitOwnHostUserInfo)(state, first, memory)) {
-            URI_FUNC(StopMalloc)(state, memory);
-            return NULL;
-        }
-        return afterLast;
-    }
-
-    switch (*first) {
-    case URI_SET_PCHAR(_UT):
-        return URI_FUNC(ParseOwnHostUserInfoNz)(state, first, afterLast, memory);
-
-    default:
-        if (!URI_FUNC(OnExitOwnHostUserInfo)(state, first, memory)) {
-            URI_FUNC(StopMalloc)(state, memory);
-            return NULL;
-        }
-        return first;
-    }
-}
-
 /*
  * [ownHostUserInfoNz]->[pctSubUnres][ownHostUserInfo]
  * [ownHostUserInfoNz]-><:>[ownPortUserInfo]
  * [ownHostUserInfoNz]-><@>[ownHost]
+ *
+ * [ownHostUserInfo]->[ownHostUserInfoNz]
+ * [ownHostUserInfo]-><NULL>
  */
 static const URI_CHAR * URI_FUNC(ParseOwnHostUserInfoNz)(URI_TYPE(ParserState) * state,
                                                          const URI_CHAR * first,
                                                          const URI_CHAR * afterLast,
                                                          UriMemoryManager * memory) {
-    if (first >= afterLast) {
-        URI_FUNC(StopSyntax)(state, afterLast, memory);
-        return NULL;
-    }
+    const URI_CHAR * const originalFirst = first;
+
+    while (first < afterLast) {
+        switch (*first) {
+        case _UT('%'):
+        case URI_SET_SUB_DELIMS(_UT):
+        case URI_SET_UNRESERVED(_UT): {
+            const URI_CHAR * const afterPctSubUnres =
+                URI_FUNC(ParsePctSubUnres)(state, first, afterLast, memory);
+            if (afterPctSubUnres == NULL) {
+                return NULL;
+            }
+            first = afterPctSubUnres;
+            break;
+        }

-    switch (*first) {
-    case _UT('%'):
-    case URI_SET_SUB_DELIMS(_UT):
-    case URI_SET_UNRESERVED(_UT): {
-        const URI_CHAR * const afterPctSubUnres =
-            URI_FUNC(ParsePctSubUnres)(state, first, afterLast, memory);
-        if (afterPctSubUnres == NULL) {
-            return NULL;
+        default:
+            goto done_looping;
+            break;
         }
-        return URI_FUNC(ParseOwnHostUserInfo)(state, afterPctSubUnres, afterLast, memory);
     }

-    case _UT(':'):
-        state->uri->hostText.afterLast = first; /* HOST END */
-        state->uri->portText.first = first + 1; /* PORT BEGIN */
-        return URI_FUNC(ParseOwnPortUserInfo)(state, first + 1, afterLast, memory);
+done_looping:
+    if (first < afterLast) {
+        switch (*first) {
+        case _UT(':'):
+            state->uri->hostText.afterLast = first; /* HOST END */
+            state->uri->portText.first = first + 1; /* PORT BEGIN */
+            return URI_FUNC(ParseOwnPortUserInfo)(state, first + 1, afterLast, memory);

-    case _UT('@'):
-        state->uri->userInfo.afterLast = first; /* USERINFO END */
-        state->uri->hostText.first = first + 1; /* HOST BEGIN */
-        return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast, memory);
+        case _UT('@'):
+            state->uri->userInfo.afterLast = first; /* USERINFO END */
+            state->uri->hostText.first = first + 1; /* HOST BEGIN */
+            return URI_FUNC(ParseOwnHost)(state, first + 1, afterLast, memory);

-    default:
-        URI_FUNC(StopSyntax)(state, first, memory);
+        default:
+            break;
+        }
+    }
+
+    if (first == originalFirst) {
+        URI_FUNC(StopSyntax)(state, afterLast, memory);
         return NULL;
     }
+
+    if (!URI_FUNC(OnExitOwnHostUserInfo)(state, first, memory)) {
+        URI_FUNC(StopMalloc)(state, memory);
+        return NULL;
+    }
+
+    return first;
 }

 static URI_INLINE UriBool URI_FUNC(OnExitOwnPortUserInfo)(URI_TYPE(ParserState) * state,
@@ -1117,6 +1094,7 @@ static const URI_CHAR * URI_FUNC(ParseOwnPortUserInfo)(URI_TYPE(ParserState) * s
                                                        const URI_CHAR * first,
                                                        const URI_CHAR * afterLast,
                                                        UriMemoryManager * memory) {
+tail_call:
     if (first >= afterLast) {
         if (!URI_FUNC(OnExitOwnPortUserInfo)(state, first, memory)) {
             URI_FUNC(StopMalloc)(state, memory);
@@ -1140,7 +1118,8 @@ static const URI_CHAR * URI_FUNC(ParseOwnPortUserInfo)(URI_TYPE(ParserState) * s
         return URI_FUNC(ParseOwnUserInfo)(state, first + 1, afterLast, memory);

     case URI_SET_DIGIT(_UT):
-        return URI_FUNC(ParseOwnPortUserInfo)(state, first + 1, afterLast, memory);
+        first += 1;
+        goto tail_call;

     case _UT('%'):
         state->uri->portText.first = NULL; /* Not a port, reset */
@@ -1176,6 +1155,7 @@ static const URI_CHAR * URI_FUNC(ParseOwnUserInfo)(URI_TYPE(ParserState) * state
                                                    const URI_CHAR * first,
                                                    const URI_CHAR * afterLast,
                                                    UriMemoryManager * memory) {
+tail_call:
     if (first >= afterLast) {
         URI_FUNC(StopSyntax)(state, afterLast, memory);
         return NULL;
@@ -1190,11 +1170,13 @@ static const URI_CHAR * URI_FUNC(ParseOwnUserInfo)(URI_TYPE(ParserState) * state
         if (afterPctSubUnres == NULL) {
             return NULL;
         }
-        return URI_FUNC(ParseOwnUserInfo)(state, afterPctSubUnres, afterLast, memory);
+        first = afterPctSubUnres;
+        goto tail_call;
     }

     case _UT(':'):
-        return URI_FUNC(ParseOwnUserInfo)(state, first + 1, afterLast, memory);
+        first += 1;
+        goto tail_call;

     case _UT('@'):
         /* SURE */
@@ -1254,6 +1236,7 @@ static const URI_CHAR * URI_FUNC(ParsePathAbsEmpty)(URI_TYPE(ParserState) * stat
                                                     const URI_CHAR * first,
                                                     const URI_CHAR * afterLast,
                                                     UriMemoryManager * memory) {
+tail_call:
     if (first >= afterLast) {
         return afterLast;
     }
@@ -1270,7 +1253,8 @@ static const URI_CHAR * URI_FUNC(ParsePathAbsEmpty)(URI_TYPE(ParserState) * stat
             URI_FUNC(StopMalloc)(state, memory);
             return NULL;
         }
-        return URI_FUNC(ParsePathAbsEmpty)(state, afterSegment, afterLast, memory);
+        first = afterSegment;
+        goto tail_call;
     }

     default:
@@ -1443,16 +1427,17 @@ static const URI_CHAR * URI_FUNC(ParsePctSubUnres)(URI_TYPE(ParserState) * state
  * [port]->[DIGIT][port]
  * [port]-><NULL>
  */
-static const URI_CHAR * URI_FUNC(ParsePort)(URI_TYPE(ParserState) * state,
-                                            const URI_CHAR * first,
+static const URI_CHAR * URI_FUNC(ParsePort)(const URI_CHAR * first,
                                             const URI_CHAR * afterLast) {
+tail_call:
     if (first >= afterLast) {
         return afterLast;
     }

     switch (*first) {
     case URI_SET_DIGIT(_UT):
-        return URI_FUNC(ParsePort)(state, first + 1, afterLast);
+        first += 1;
+        goto tail_call;

     default:
         return first;
@@ -1469,6 +1454,7 @@ static const URI_CHAR * URI_FUNC(ParseQueryFrag)(URI_TYPE(ParserState) * state,
                                                  const URI_CHAR * first,
                                                  const URI_CHAR * afterLast,
                                                  UriMemoryManager * memory) {
+tail_call:
     if (first >= afterLast) {
         return afterLast;
     }
@@ -1480,12 +1466,14 @@ static const URI_CHAR * URI_FUNC(ParseQueryFrag)(URI_TYPE(ParserState) * state,
         if (afterPchar == NULL) {
             return NULL;
         }
-        return URI_FUNC(ParseQueryFrag)(state, afterPchar, afterLast, memory);
+        first = afterPchar;
+        goto tail_call;
     }

     case _UT('/'):
     case _UT('?'):
-        return URI_FUNC(ParseQueryFrag)(state, first + 1, afterLast, memory);
+        first += 1;
+        goto tail_call;

     default:
         return first;
@@ -1500,6 +1488,7 @@ static const URI_CHAR * URI_FUNC(ParseSegment)(URI_TYPE(ParserState) * state,
                                                const URI_CHAR * first,
                                                const URI_CHAR * afterLast,
                                                UriMemoryManager * memory) {
+tail_call:
     if (first >= afterLast) {
         return afterLast;
     }
@@ -1511,7 +1500,8 @@ static const URI_CHAR * URI_FUNC(ParseSegment)(URI_TYPE(ParserState) * state,
         if (afterPchar == NULL) {
             return NULL;
         }
-        return URI_FUNC(ParseSegment)(state, afterPchar, afterLast, memory);
+        first = afterPchar;
+        goto tail_call;
     }

     default:
@@ -1572,6 +1562,7 @@ static const URI_CHAR * URI_FUNC(ParseSegmentNzNcOrScheme2)(URI_TYPE(ParserState
                                                             const URI_CHAR * first,
                                                             const URI_CHAR * afterLast,
                                                             UriMemoryManager * memory) {
+tail_call:
     if (first >= afterLast) {
         if (!URI_FUNC(OnExitSegmentNzNcOrScheme2)(state, first, memory)) {
             URI_FUNC(StopMalloc)(state, memory);
@@ -1586,7 +1577,8 @@ static const URI_CHAR * URI_FUNC(ParseSegmentNzNcOrScheme2)(URI_TYPE(ParserState
     case _UT('-'):
     case URI_SET_ALPHA(_UT):
     case URI_SET_DIGIT(_UT):
-        return URI_FUNC(ParseSegmentNzNcOrScheme2)(state, first + 1, afterLast, memory);
+        first += 1;
+        goto tail_call;

     case _UT('%'): {
         const URI_CHAR * const afterPctEncoded =
@@ -1796,6 +1788,7 @@ static const URI_CHAR * URI_FUNC(ParseZeroMoreSlashSegs)(URI_TYPE(ParserState) *
                                                          const URI_CHAR * first,
                                                          const URI_CHAR * afterLast,
                                                          UriMemoryManager * memory) {
+tail_call:
     if (first >= afterLast) {
         return afterLast;
     }
@@ -1812,7 +1805,8 @@ static const URI_CHAR * URI_FUNC(ParseZeroMoreSlashSegs)(URI_TYPE(ParserState) *
             URI_FUNC(StopMalloc)(state, memory);
             return NULL;
         }
-        return URI_FUNC(ParseZeroMoreSlashSegs)(state, afterSegment, afterLast, memory);
+        first = afterSegment;
+        goto tail_call;
     }

     default: