Skip to content

Commit d25aac2

Browse files
committed
Fixed GH-16944: Refactor IP ranges by using the tables from RFC 6890
1 parent f2fdcfc commit d25aac2

File tree

3 files changed

+145
-81
lines changed

3 files changed

+145
-81
lines changed

NEWS

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ PHP NEWS
1515
- FFI:
1616
. Fixed bug #79075 (FFI header parser chokes on comments). (nielsdos)
1717

18+
- Filter:
19+
. Fixed bug GH-16944 (Fix filtering special IPv4 and IPv6 ranges, by using
20+
information from RFC 6890). (Derick)
21+
1822
- FPM:
1923
. Fixed bug GH-13437 (FPM: ERROR: scoreboard: failed to lock (already
2024
locked)). (Jakub Zelenka)

ext/filter/logical_filters.c

+131-81
Original file line numberDiff line numberDiff line change
@@ -869,15 +869,115 @@ static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8])
869869
}
870870
/* }}} */
871871

872-
void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
872+
/* From the tables in RFC 6890 - Special-Purpose IP Address Registries */
873+
static bool ipv4_get_status_flags(const int ip[8], bool *global, bool *reserved, bool *private)
874+
{
875+
*global = false;
876+
*reserved = false;
877+
*private = false;
878+
879+
if (ip[0] == 0) {
880+
/* RFC 1122 - This host on this network */
881+
*reserved = true;
882+
} else if (ip[0] == 10) {
883+
/* RFC 1918 - Private Use */
884+
*private = true;
885+
} else if (ip[0] == 100 && ip[1] >= 64 && ip[1] <= 127) {
886+
/* RFC 6598 - Shared Address Space */
887+
} else if (ip[0] == 127) {
888+
/* RFC 1122 - Loopback */
889+
*reserved = true;
890+
} else if (ip[0] == 169 && ip[1] == 254) {
891+
/* RFC 3927 - Link Local */
892+
*reserved = true;
893+
} else if (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) {
894+
/* RFC 1918 - Private Use */
895+
*private = true;
896+
} else if (ip[0] == 192 && ip[1] == 0 && ip[2] == 0) {
897+
/* RFC 6890 - IETF Protocol Assignments */
898+
} else if (ip[0] == 192 && ip[1] == 0 && ip[2] == 0 && ip[3] >= 0 && ip[3] <= 7) {
899+
/* RFC 6333 - DS-Lite */
900+
} else if (ip[0] == 192 && ip[1] == 0 && ip[2] == 2) {
901+
/* RFC 5737 - Documentation */
902+
} else if (ip[0] == 192 && ip[1] == 88 && ip[2] == 99) {
903+
/* RFC 3068 - 6to4 Relay Anycast */
904+
*global = true;
905+
} else if (ip[0] == 192 && ip[1] == 168) {
906+
/* RFC 1918 - Private Use */
907+
*private = true;
908+
} else if (ip[0] == 198 && ip[1] >= 18 && ip[1] <= 19) {
909+
/* RFC 2544 - Benchmarking */
910+
} else if (ip[0] == 198 && ip[1] == 51 && ip[2] == 100) {
911+
/* RFC 5737 - Documentation */
912+
} else if (ip[0] == 203 && ip[1] == 0 && ip[2] == 113) {
913+
/* RFC 5737 - Documentation */
914+
} else if (ip[0] >= 240 && ip[1] <= 255) {
915+
/* RFC 1122 - Reserved */
916+
*reserved = true;
917+
} else if (ip[0] == 255 && ip[1] == 255 && ip[2] == 255 && ip[3] == 255) {
918+
/* RFC 0919 - Limited Broadcast */
919+
} else {
920+
return false;
921+
}
922+
923+
return true;
924+
}
925+
926+
/* From the tables in RFC 6890 - Special-Purpose IP Address Registries */
927+
static bool ipv6_get_status_flags(const int ip[8], bool *global, bool *reserved, bool *private)
873928
{
874-
/* validates an ipv4 or ipv6 IP, based on the flag (4, 6, or both) add a
875-
* flag to throw out reserved ranges; multicast ranges... etc. If both
876-
* allow_ipv4 and allow_ipv6 flags flag are used, then the first dot or
877-
* colon determine the format */
929+
*global = false;
930+
*reserved = false;
931+
*private = false;
932+
933+
if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && ip[7] == 0) {
934+
/* RFC 4291 - Unspecified Address */
935+
*reserved = true;
936+
} else if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && ip[7] == 1) {
937+
/* RFC 4291 - Loopback Address */
938+
*reserved = true;
939+
} else if (ip[0] == 0x0064 && ip[1] == 0xff9b) {
940+
/* RFC 6052 - IPv4-IPv6 Translation */
941+
*global = true;
942+
} else if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0xffff) {
943+
/* RFC 4291 - IPv4-mapped Address */
944+
*reserved = true;
945+
} else if (ip[0] == 0x0100 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) {
946+
/* RFC 6666 - Discard-Only Address Block */
947+
} else if (ip[0] == 0x2001 && ip[1] == 0x0000) {
948+
/* RFC 4380 - TEREDO */
949+
} else if (ip[0] == 0x2001 && ip[1] <= 0x01ff) {
950+
/* RFC 2928 - IETF Protocol Assignments */
951+
} else if (ip[0] == 0x2001 && ip[1] == 0x0002 && ip[2] == 0) {
952+
/* RFC 5180 - Benchmarking */
953+
} else if (ip[0] == 0x2001 && ip[1] == 0x0db8) {
954+
/* RFC 3849 - Documentation */
955+
} else if (ip[0] == 0x2001 && ip[1] >= 0x0010 && ip[1] <= 0x001f) {
956+
/* RFC 4843 - ORCHID */
957+
} else if (ip[0] == 0x2002) {
958+
/* RFC 3056 - 6to4 */
959+
} else if (ip[0] >= 0xfc00 && ip[0] <= 0xfdff) {
960+
/* RFC 4193 - Unique-Local */
961+
*private = true;
962+
} else if (ip[0] >= 0xfe80 && ip[0] <= 0xfebf) {
963+
/* RFC 4291 - Linked-Scoped Unicast */
964+
*reserved = true;
965+
} else {
966+
return false;
967+
}
878968

879-
int ip[8];
880-
int mode;
969+
return true;
970+
}
971+
972+
/* Validates an ipv4 or ipv6 IP, based on the flag (4, 6, or both) add a flag
973+
* to throw out reserved ranges; multicast ranges... etc. If both allow_ipv4
974+
* and allow_ipv6 flags flag are used, then the first dot or colon determine
975+
* the format */
976+
void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
977+
{
978+
int ip[8];
979+
int mode;
980+
bool flag_global, flag_reserved, flag_private; /* flags for ranges as determined by RFC 6890 */
881981

882982
if (memchr(Z_STRVAL_P(value), ':', Z_STRLEN_P(value))) {
883983
mode = FORMAT_IPV6;
@@ -895,85 +995,35 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
895995
RETURN_VALIDATION_FAILED
896996
}
897997

898-
switch (mode) {
899-
case FORMAT_IPV4:
900-
if (!_php_filter_validate_ipv4(Z_STRVAL_P(value), Z_STRLEN_P(value), ip)) {
901-
RETURN_VALIDATION_FAILED
902-
}
998+
if (mode == FORMAT_IPV4) {
999+
if (!_php_filter_validate_ipv4(Z_STRVAL_P(value), Z_STRLEN_P(value), ip)) {
1000+
RETURN_VALIDATION_FAILED
1001+
}
9031002

904-
/* Check flags */
905-
if (flags & FILTER_FLAG_NO_PRIV_RANGE || flags & FILTER_FLAG_GLOBAL_RANGE) {
906-
if (
907-
(ip[0] == 10) ||
908-
(ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) ||
909-
(ip[0] == 192 && ip[1] == 168)
910-
) {
911-
RETURN_VALIDATION_FAILED
912-
}
913-
}
1003+
if (!ipv4_get_status_flags(ip, &flag_global, &flag_reserved, &flag_private)) {
1004+
return; /* no special block */
1005+
}
1006+
}
1007+
else if (mode == FORMAT_IPV6) {
1008+
if (_php_filter_validate_ipv6(Z_STRVAL_P(value), Z_STRLEN_P(value), ip) < 1) {
1009+
RETURN_VALIDATION_FAILED
1010+
}
9141011

915-
if (flags & FILTER_FLAG_NO_RES_RANGE || flags & FILTER_FLAG_GLOBAL_RANGE) {
916-
if (
917-
(ip[0] == 0) ||
918-
(ip[0] >= 240) ||
919-
(ip[0] == 127) ||
920-
(ip[0] == 169 && ip[1] == 254)
921-
) {
922-
RETURN_VALIDATION_FAILED
923-
}
924-
}
1012+
if (!ipv6_get_status_flags(ip, &flag_global, &flag_reserved, &flag_private)) {
1013+
return; /* no special block */
1014+
}
1015+
}
9251016

926-
if (flags & FILTER_FLAG_GLOBAL_RANGE) {
927-
if (
928-
(ip[0] == 100 && ip[1] >= 64 && ip[1] <= 127 ) ||
929-
(ip[0] == 192 && ip[1] == 0 && ip[2] == 0 ) ||
930-
(ip[0] == 192 && ip[1] == 0 && ip[2] == 2 ) ||
931-
(ip[0] == 198 && ip[1] >= 18 && ip[1] <= 19 ) ||
932-
(ip[0] == 198 && ip[1] == 51 && ip[2] == 100 ) ||
933-
(ip[0] == 203 && ip[1] == 0 && ip[2] == 113 )
934-
) {
935-
RETURN_VALIDATION_FAILED
936-
}
937-
}
1017+
if ((flags & FILTER_FLAG_GLOBAL_RANGE) && flag_global != true) {
1018+
RETURN_VALIDATION_FAILED
1019+
}
9381020

939-
break;
1021+
if ((flags & FILTER_FLAG_NO_PRIV_RANGE) && flag_private == true) {
1022+
RETURN_VALIDATION_FAILED
1023+
}
9401024

941-
case FORMAT_IPV6:
942-
{
943-
int res = 0;
944-
res = _php_filter_validate_ipv6(Z_STRVAL_P(value), Z_STRLEN_P(value), ip);
945-
if (res < 1) {
946-
RETURN_VALIDATION_FAILED
947-
}
948-
/* Check flags */
949-
if (flags & FILTER_FLAG_NO_PRIV_RANGE || flags & FILTER_FLAG_GLOBAL_RANGE) {
950-
if (ip[0] >= 0xfc00 && ip[0] <= 0xfdff) {
951-
RETURN_VALIDATION_FAILED
952-
}
953-
}
954-
if (flags & FILTER_FLAG_NO_RES_RANGE || flags & FILTER_FLAG_GLOBAL_RANGE) {
955-
if (
956-
(ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && (ip[7] == 0 || ip[7] == 1)) ||
957-
(ip[0] >= 0xfe80 && ip[0] <= 0xfebf) ||
958-
(ip[0] == 0x2001 && (ip[1] == 0x0db8 || (ip[1] >= 0x0010 && ip[1] <= 0x001f)))
959-
960-
) {
961-
RETURN_VALIDATION_FAILED
962-
}
963-
}
964-
if (flags & FILTER_FLAG_GLOBAL_RANGE) {
965-
if (
966-
(ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0xffff) ||
967-
(ip[0] == 0x0100 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) ||
968-
(ip[0] == 0x2001 && ip[1] <= 0x01ff) ||
969-
(ip[0] == 0x2001 && ip[1] == 0x0002 && ip[2] == 0) ||
970-
(ip[0] >= 0xfc00 && ip[0] <= 0xfdff)
971-
) {
972-
RETURN_VALIDATION_FAILED
973-
}
974-
}
975-
}
976-
break;
1025+
if ((flags & FILTER_FLAG_NO_RES_RANGE) && flag_reserved == true) {
1026+
RETURN_VALIDATION_FAILED
9771027
}
9781028
}
9791029
/* }}} */

ext/filter/tests/gh16944.phpt

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Bug GH-16944 (Invalid filtering of IPv6 with FILTER_FLAG_NO_RES_RANGE)
3+
--EXTENSIONS--
4+
filter
5+
--FILE--
6+
<?php
7+
var_dump(filter_var("::ffff:0:1", FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE));
8+
?>
9+
--EXPECT--
10+
bool(false)

0 commit comments

Comments
 (0)