diff --git a/ext/standard/tests/url/gh12703.phpt b/ext/standard/tests/url/gh12703.phpt new file mode 100644 index 0000000000000..95a5e151c09d2 --- /dev/null +++ b/ext/standard/tests/url/gh12703.phpt @@ -0,0 +1,104 @@ +--TEST-- +GH-12703 (parse_url mishandles colon inside path) +--FILE-- + +--EXPECT-- +array(1) { + ["path"]=> + string(7) "/page:1" +} +string(7) "/page:1" +NULL +array(2) { + ["path"]=> + string(7) "/page:1" + ["query"]=> + string(7) "foo=bar" +} +array(1) { + ["path"]=> + string(2) "/:" +} +array(1) { + ["path"]=> + string(4) "/:80" +} +array(2) { + ["host"]=> + string(15) "www.example.com" + ["path"]=> + string(11) "/foo:65535/" +} +array(2) { + ["host"]=> + string(15) "www.example.com" + ["path"]=> + string(7) "/foo:1/" +} +array(2) { + ["host"]=> + string(15) "www.example.com" + ["path"]=> + string(11) "/foo:65536/" +} +array(2) { + ["host"]=> + string(4) "host" + ["path"]=> + string(9) "/a:1/b:2/" +} +array(3) { + ["host"]=> + string(15) "www.example.com" + ["port"]=> + int(8080) + ["path"]=> + string(11) "/foo:65535/" +} +array(8) { + ["scheme"]=> + string(6) "scheme" + ["host"]=> + string(4) "host" + ["port"]=> + int(8080) + ["user"]=> + string(4) "user" + ["pass"]=> + string(4) "pass" + ["path"]=> + string(8) "/a:1/b:2" + ["query"]=> + string(3) "q=1" + ["fragment"]=> + string(1) "f" +} diff --git a/ext/standard/url.c b/ext/standard/url.c index 4ddf7f80c64f9..cfc7a74d1c4be 100644 --- a/ext/standard/url.c +++ b/ext/standard/url.c @@ -54,9 +54,15 @@ static void php_replace_controlchars(char *str, size_t len) ZEND_ASSERT(str != NULL); + /* Replace ASCII C0 control chars (0x00..0x1F) and DEL (0x7F). An inline + * comparison is used instead of iscntrl() because (a) it avoids the + * per-byte locale lookup through __ctype_b_loc(), and (b) URL components + * are bytes, not locale-dependent text, so the C-locale semantics are + * what we want regardless of the process locale. The compiler can also + * auto-vectorize this simple form. */ while (s < e) { - if (iscntrl(*s)) { - *s='_'; + if (UNEXPECTED(*s < 0x20 || *s == 0x7f)) { + *s = '_'; } s++; } @@ -104,7 +110,7 @@ PHPAPI php_url *php_url_parse_ex2(char const *str, size_t length, bool *has_port while (p < e) { /* scheme = 1*[ lowalpha | digit | "+" | "-" | "." ] */ if (!isalpha(*p) && !isdigit(*p) && *p != '+' && *p != '.' && *p != '-') { - if (e + 1 < ue && e < binary_strcspn(s, ue, "?#")) { + if (*s != '/' && e + 1 < ue && e < binary_strcspn(s, ue, "?#")) { goto parse_port; } else if (s + 1 < ue && *s == '/' && *(s + 1) == '/') { /* relative-scheme URL */ s += 2;