Skip to content

Commit 81b501f

Browse files
committed
Merge branch 'PHP-8.1'
* PHP-8.1: Fix #61700: FILTER_FLAG_IPV6/FILTER_FLAG_NO_PRIV|RES_RANGE failing
2 parents a22f9fc + 1dcc0ff commit 81b501f

File tree

3 files changed

+74
-52
lines changed

3 files changed

+74
-52
lines changed

ext/filter/logical_filters.c

Lines changed: 57 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
#define FORMAT_IPV4 4
9090
#define FORMAT_IPV6 6
9191

92-
static int _php_filter_validate_ipv6(char *str, size_t str_len);
92+
static int _php_filter_validate_ipv6(char *str, size_t str_len, int ip[8]);
9393

9494
static int php_filter_parse_int(const char *str, size_t str_len, zend_long *ret) { /* {{{ */
9595
zend_long ctx_value;
@@ -613,7 +613,7 @@ void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
613613
t = e - 1;
614614

615615
/* An IPv6 enclosed by square brackets is a valid hostname */
616-
if (*s == '[' && *t == ']' && _php_filter_validate_ipv6((s + 1), l - 2)) {
616+
if (*s == '[' && *t == ']' && _php_filter_validate_ipv6((s + 1), l - 2, NULL)) {
617617
php_url_free(url);
618618
return;
619619
}
@@ -753,11 +753,11 @@ static int _php_filter_validate_ipv4(char *str, size_t str_len, int *ip) /* {{{
753753
}
754754
/* }}} */
755755

756-
static int _php_filter_validate_ipv6(char *str, size_t str_len) /* {{{ */
756+
static int _php_filter_validate_ipv6(char *str, size_t str_len, int ip[8]) /* {{{ */
757757
{
758-
int compressed = 0;
758+
int compressed_pos = -1;
759759
int blocks = 0;
760-
int n;
760+
int num, n, i;
761761
char *ipv4;
762762
char *end;
763763
int ip4elm[4];
@@ -800,35 +800,67 @@ static int _php_filter_validate_ipv6(char *str, size_t str_len) /* {{{ */
800800
return 0;
801801
}
802802
if (*str == ':') {
803-
if (compressed) {
803+
if (compressed_pos >= 0) {
804804
return 0;
805805
}
806-
blocks++; /* :: means 1 or more 16-bit 0 blocks */
807-
compressed = 1;
808-
806+
if (ip && blocks < 8) {
807+
ip[blocks] = -1;
808+
}
809+
compressed_pos = blocks++; /* :: means 1 or more 16-bit 0 blocks */
809810
if (++str == end) {
810-
return (blocks <= 8);
811+
if (blocks > 8) {
812+
return 0;
813+
}
814+
goto fixup_ip;
811815
}
812816
} else if ((str - 1) == s) {
813817
/* don't allow leading : without another : following */
814818
return 0;
815819
}
816820
}
817-
n = 0;
818-
while ((str < end) &&
819-
((*str >= '0' && *str <= '9') ||
820-
(*str >= 'a' && *str <= 'f') ||
821-
(*str >= 'A' && *str <= 'F'))) {
821+
num = n = 0;
822+
while (str < end) {
823+
if (*str >= '0' && *str <= '9') {
824+
num = 16 * num + (*str - '0');
825+
} else if (*str >= 'a' && *str <= 'f') {
826+
num = 16 * num + (*str - 'a') + 10;
827+
} else if (*str >= 'A' && *str <= 'F') {
828+
num = 16 * num + (*str - 'A') + 10;
829+
} else {
830+
break;
831+
}
822832
n++;
823833
str++;
824834
}
835+
if (ip && blocks < 8) {
836+
ip[blocks] = num;
837+
}
825838
if (n < 1 || n > 4) {
826839
return 0;
827840
}
828841
if (++blocks > 8)
829842
return 0;
830843
}
831-
return ((compressed && blocks <= 8) || blocks == 8);
844+
845+
fixup_ip:
846+
if (ip && ipv4) {
847+
for (i = 0; i < 5; i++) {
848+
ip[i] = 0;
849+
}
850+
ip[i++] = 0xffff;
851+
ip[i++] = 256 * ip4elm[0] + ip4elm[1];
852+
ip[i++] = 256 * ip4elm[2] + ip4elm[3];
853+
} else if (ip && compressed_pos >= 0 && blocks <= 8) {
854+
int offset = 8 - blocks;
855+
for (i = 7; i > compressed_pos + offset; i--) {
856+
ip[i] = ip[i - offset];
857+
}
858+
for (i = compressed_pos + offset; i >= compressed_pos; i--) {
859+
ip[i] = 0;
860+
}
861+
}
862+
863+
return (compressed_pos >= 0 && blocks <= 8) || blocks == 8;
832864
}
833865
/* }}} */
834866

@@ -839,7 +871,7 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
839871
* allow_ipv4 and allow_ipv6 flags flag are used, then the first dot or
840872
* colon determine the format */
841873

842-
int ip[4];
874+
int ip[8];
843875
int mode;
844876

845877
if (memchr(Z_STRVAL_P(value), ':', Z_STRLEN_P(value))) {
@@ -890,52 +922,28 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
890922
case FORMAT_IPV6:
891923
{
892924
int res = 0;
893-
res = _php_filter_validate_ipv6(Z_STRVAL_P(value), Z_STRLEN_P(value));
925+
res = _php_filter_validate_ipv6(Z_STRVAL_P(value), Z_STRLEN_P(value), ip);
894926
if (res < 1) {
895927
RETURN_VALIDATION_FAILED
896928
}
897929
/* Check flags */
898930
if (flags & FILTER_FLAG_NO_PRIV_RANGE) {
899-
if (Z_STRLEN_P(value) >=2 && (!strncasecmp("FC", Z_STRVAL_P(value), 2) || !strncasecmp("FD", Z_STRVAL_P(value), 2))) {
931+
if (ip[0] >= 0xfc00 && ip[0] <= 0xfdff) {
900932
RETURN_VALIDATION_FAILED
901933
}
902934
}
903935
if (flags & FILTER_FLAG_NO_RES_RANGE) {
904-
switch (Z_STRLEN_P(value)) {
905-
case 1: case 0:
906-
break;
907-
case 2:
908-
if (zend_string_equals_literal(Z_STR_P(value), "::")) {
909-
RETURN_VALIDATION_FAILED
910-
}
911-
break;
912-
case 3:
913-
if (zend_string_equals_literal(Z_STR_P(value), "::1") || zend_string_equals_literal(Z_STR_P(value), "5f:")) {
914-
RETURN_VALIDATION_FAILED
915-
}
916-
break;
917-
default:
918-
if (Z_STRLEN_P(value) >= 5) {
919-
if (
920-
!strncasecmp("fe8", Z_STRVAL_P(value), 3) ||
921-
!strncasecmp("fe9", Z_STRVAL_P(value), 3) ||
922-
!strncasecmp("fea", Z_STRVAL_P(value), 3) ||
923-
!strncasecmp("feb", Z_STRVAL_P(value), 3)
936+
if ((ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0
937+
&& ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && (ip[7] == 0 || ip[7] == 1))
938+
|| (ip[0] == 0x5f)
939+
|| (ip[0] >= 0xfe80 && ip[0] <= 0xfebf)
940+
|| ((ip[0] == 0x2001 && ip[1] == 0x0db8) || (ip[1] >= 0x0010 && ip[1] <= 0x001f))
941+
|| (ip[0] == 0x3ff3)
924942
) {
925943
RETURN_VALIDATION_FAILED
926944
}
927945
}
928-
if (
929-
(Z_STRLEN_P(value) >= 9 && !strncasecmp("2001:0db8", Z_STRVAL_P(value), 9)) ||
930-
(Z_STRLEN_P(value) >= 2 && !strncasecmp("5f", Z_STRVAL_P(value), 2)) ||
931-
(Z_STRLEN_P(value) >= 4 && !strncasecmp("3ff3", Z_STRVAL_P(value), 4)) ||
932-
(Z_STRLEN_P(value) >= 8 && !strncasecmp("2001:001", Z_STRVAL_P(value), 8))
933-
) {
934-
RETURN_VALIDATION_FAILED
935946
}
936-
}
937-
}
938-
}
939947
break;
940948
}
941949
}

ext/filter/tests/bug47435.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ var_dump(filter_var("::", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6));
1010
var_dump(filter_var("::", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE));
1111
var_dump(filter_var("::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6));
1212
var_dump(filter_var("::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE));
13-
var_dump(filter_var("fe8:5:6::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6));
14-
var_dump(filter_var("fe8:5:6::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE));
13+
var_dump(filter_var("fe80:5:6::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6));
14+
var_dump(filter_var("fe80:5:6::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE));
1515
var_dump(filter_var("2001:0db8::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6));
1616
var_dump(filter_var("2001:0db8::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE));
1717
var_dump(filter_var("5f::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6));
@@ -26,7 +26,7 @@ string(2) "::"
2626
bool(false)
2727
string(3) "::1"
2828
bool(false)
29-
string(10) "fe8:5:6::1"
29+
string(11) "fe80:5:6::1"
3030
bool(false)
3131
string(12) "2001:0db8::1"
3232
bool(false)

ext/filter/tests/bug61700.phpt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
Bug #61700 (FILTER_FLAG_IPV6/FILTER_FLAG_NO_PRIV|RES_RANGE failing)
3+
--EXTENSIONS--
4+
filter
5+
--FILE--
6+
<?php
7+
var_dump(filter_var('::ffff:192.168.1.1', FILTER_VALIDATE_IP, FILTER_FLAG_IPV4));
8+
var_dump(filter_var('::ffff:192.168.1.1', FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE));
9+
var_dump(filter_var('0:0:0:0:0:0:0:1', FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE));
10+
?>
11+
--EXPECT--
12+
bool(false)
13+
string(18) "::ffff:192.168.1.1"
14+
bool(false)

0 commit comments

Comments
 (0)