From c84dde148f8d82232a110f9ff7c80df94d0d6c8c Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 5 Apr 2019 16:28:41 +0400 Subject: [PATCH] MDEV-19184 Crash in IS_IPV6(_ucs2 0x0031) --- mysql-test/main/ctype_ucs.result | 24 +++++++ mysql-test/main/ctype_ucs.test | 15 +++++ sql/item_inetfunc.cc | 103 ++++++++++++++++++++----------- 3 files changed, 107 insertions(+), 35 deletions(-) diff --git a/mysql-test/main/ctype_ucs.result b/mysql-test/main/ctype_ucs.result index db09bf116fc..a59a25db4e6 100644 --- a/mysql-test/main/ctype_ucs.result +++ b/mysql-test/main/ctype_ucs.result @@ -6393,5 +6393,29 @@ SELECT HEX(INET6_ATON('1::1')), HEX(INET6_ATON(CONVERT('1::1' USING ucs2))); HEX(INET6_ATON('1::1')) HEX(INET6_ATON(CONVERT('1::1' USING ucs2))) 00010000000000000000000000000001 00010000000000000000000000000001 # +# MDEV-19184 Crash in IS_IPV6(_ucs2 0x0031) +# +SET NAMES utf8; +SELECT IS_IPV6(_ucs2 0x0031); +IS_IPV6(_ucs2 0x0031) +0 +SELECT IS_IPV4(_ucs2 0x0031); +IS_IPV4(_ucs2 0x0031) +0 +SELECT IS_IPV6(_ucs2 0x003A003A); +IS_IPV6(_ucs2 0x003A003A) +1 +SELECT IS_IPV4(_ucs2 0x00310030002E0030002E0030002E0031); +IS_IPV4(_ucs2 0x00310030002E0030002E0030002E0031) +1 +SET NAMES utf8, collation_connection=ucs2_bin; +SELECT IS_IPV6('::'); +IS_IPV6('::') +1 +SELECT IS_IPV4('10.0.0.1'); +IS_IPV4('10.0.0.1') +1 +SET NAMES utf8; +# # End of 10.4 tests # diff --git a/mysql-test/main/ctype_ucs.test b/mysql-test/main/ctype_ucs.test index 983f48edbc5..7a772a092b1 100644 --- a/mysql-test/main/ctype_ucs.test +++ b/mysql-test/main/ctype_ucs.test @@ -1105,6 +1105,21 @@ DEALLOCATE PREPARE stmt; SELECT HEX(INET6_ATON('1::1')), HEX(INET6_ATON(CONVERT('1::1' USING ucs2))); +--echo # +--echo # MDEV-19184 Crash in IS_IPV6(_ucs2 0x0031) +--echo # + +SET NAMES utf8; +SELECT IS_IPV6(_ucs2 0x0031); +SELECT IS_IPV4(_ucs2 0x0031); + +SELECT IS_IPV6(_ucs2 0x003A003A); +SELECT IS_IPV4(_ucs2 0x00310030002E0030002E0030002E0031); + +SET NAMES utf8, collation_connection=ucs2_bin; +SELECT IS_IPV6('::'); +SELECT IS_IPV4('10.0.0.1'); +SET NAMES utf8; --echo # --echo # End of 10.4 tests diff --git a/sql/item_inetfunc.cc b/sql/item_inetfunc.cc index f5fcbf65309..379897ac8c1 100644 --- a/sql/item_inetfunc.cc +++ b/sql/item_inetfunc.cc @@ -22,9 +22,18 @@ /////////////////////////////////////////////////////////////////////////// static const size_t IN_ADDR_SIZE= 4; +static const size_t IN_ADDR_MAX_CHAR_LENGTH= 15; + static const size_t IN6_ADDR_SIZE= 16; static const size_t IN6_ADDR_NUM_WORDS= IN6_ADDR_SIZE / 2; +/** + Non-abbreviated syntax is 8 groups, up to 4 digits each, + plus 7 delimiters between the groups. + Abbreviated syntax is even shorter. +*/ +static const uint IN6_ADDR_MAX_CHAR_LENGTH= 8 * 4 + 7; + static const char HEX_DIGITS[]= "0123456789abcdef"; /////////////////////////////////////////////////////////////////////////// @@ -143,7 +152,20 @@ class Inet4 { char m_buffer[IN_ADDR_SIZE]; protected: - bool str_to_ipv4(const char *str, size_t length, CHARSET_INFO *cs); + bool ascii_to_ipv4(const char *str, size_t length); + bool character_string_to_ipv4(const char *str, size_t str_length, + CHARSET_INFO *cs) + { + if (cs->state & MY_CS_NONASCII) + { + char tmp[IN_ADDR_MAX_CHAR_LENGTH]; + String_copier copier; + uint length= copier.well_formed_copy(&my_charset_latin1, tmp, sizeof(tmp), + cs, str, str_length); + return ascii_to_ipv4(tmp, length); + } + return ascii_to_ipv4(str, str_length); + } bool binary_to_ipv4(const char *str, size_t length) { if (length != sizeof(m_buffer)) @@ -180,7 +202,7 @@ class Inet4_null: public Inet4, public Null_flag public: // Initialize from a text representation Inet4_null(const char *str, size_t length, CHARSET_INFO *cs) - :Null_flag(str_to_ipv4(str, length, cs)) + :Null_flag(character_string_to_ipv4(str, length, cs)) { } Inet4_null(const String &str) :Inet4_null(str.ptr(), str.length(), str.charset()) @@ -222,7 +244,20 @@ class Inet6 char m_buffer[IN6_ADDR_SIZE]; protected: bool make_from_item(Item *item); - bool str_to_ipv6(const char *str, size_t str_length, CHARSET_INFO *cs); + bool ascii_to_ipv6(const char *str, size_t str_length); + bool character_string_to_ipv6(const char *str, size_t str_length, + CHARSET_INFO *cs) + { + if (cs->state & MY_CS_NONASCII) + { + char tmp[IN6_ADDR_MAX_CHAR_LENGTH]; + String_copier copier; + uint length= copier.well_formed_copy(&my_charset_latin1, tmp, sizeof(tmp), + cs, str, str_length); + return ascii_to_ipv6(tmp, length); + } + return ascii_to_ipv6(str, str_length); + } bool binary_to_ipv6(const char *str, size_t length) { if (length != sizeof(m_buffer)) @@ -264,7 +299,7 @@ class Inet6_null: public Inet6, public Null_flag public: // Initialize from a text representation Inet6_null(const char *str, size_t length, CHARSET_INFO *cs) - :Null_flag(str_to_ipv6(str, length, cs)) + :Null_flag(character_string_to_ipv6(str, length, cs)) { } Inet6_null(const String &str) :Inet6_null(str.ptr(), str.length(), str.charset()) @@ -343,20 +378,19 @@ bool Inet6::make_from_item(Item *item) IPv4-part differently on different platforms. */ -bool Inet4::str_to_ipv4(const char *str, size_t str_length, CHARSET_INFO *cs) +bool Inet4::ascii_to_ipv4(const char *str, size_t str_length) { - DBUG_ASSERT(cs->mbminlen == 1); if (str_length < 7) { - DBUG_PRINT("error", ("str_to_ipv4(%.*s): " + DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): " "invalid IPv4 address: too short.", (int) str_length, str)); return true; } - if (str_length > 15) + if (str_length > IN_ADDR_MAX_CHAR_LENGTH) { - DBUG_PRINT("error", ("str_to_ipv4(%.*s): " + DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): " "invalid IPv4 address: too long.", (int) str_length, str)); return true; @@ -380,7 +414,7 @@ bool Inet4::str_to_ipv4(const char *str, size_t str_length, CHARSET_INFO *cs) if (chars_in_group > 3) { - DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " + DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: " "too many characters in a group.", (int) str_length, str)); return true; @@ -390,7 +424,7 @@ bool Inet4::str_to_ipv4(const char *str, size_t str_length, CHARSET_INFO *cs) if (byte_value > 255) { - DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " + DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: " "invalid byte value.", (int) str_length, str)); return true; @@ -400,7 +434,7 @@ bool Inet4::str_to_ipv4(const char *str, size_t str_length, CHARSET_INFO *cs) { if (chars_in_group == 0) { - DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " + DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: " "too few characters in a group.", (int) str_length, str)); return true; @@ -414,14 +448,14 @@ bool Inet4::str_to_ipv4(const char *str, size_t str_length, CHARSET_INFO *cs) if (dot_count > 3) { - DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " + DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: " "too many dots.", (int) str_length, str)); return true; } } else { - DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " + DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: " "invalid character at pos %d.", (int) str_length, str, (int) (p - str))); return true; @@ -430,14 +464,14 @@ bool Inet4::str_to_ipv4(const char *str, size_t str_length, CHARSET_INFO *cs) if (c == '.') { - DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " + DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: " "ending at '.'.", (int) str_length, str)); return true; } if (dot_count != 3) { - DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " + DBUG_PRINT("error", ("ascii_to_ipv4(%.*s): invalid IPv4 address: " "too few groups.", (int) str_length, str)); return true; @@ -445,7 +479,7 @@ bool Inet4::str_to_ipv4(const char *str, size_t str_length, CHARSET_INFO *cs) ipv4_bytes[3]= (unsigned char) byte_value; - DBUG_PRINT("info", ("str_to_ipv4(%.*s): valid IPv4 address: %d.%d.%d.%d", + DBUG_PRINT("info", ("ascii_to_ipv4(%.*s): valid IPv4 address: %d.%d.%d.%d", (int) str_length, str, ipv4_bytes[0], ipv4_bytes[1], ipv4_bytes[2], ipv4_bytes[3])); @@ -468,20 +502,18 @@ bool Inet4::str_to_ipv4(const char *str, size_t str_length, CHARSET_INFO *cs) IPv4-part differently on different platforms. */ -bool Inet6::str_to_ipv6(const char *str, size_t str_length, CHARSET_INFO *cs) +bool Inet6::ascii_to_ipv6(const char *str, size_t str_length) { - DBUG_ASSERT(cs->mbminlen == 1); - if (str_length < 2) { - DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: too short.", + DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: too short.", (int) str_length, str)); return true; } - if (str_length > 8 * 4 + 7) + if (str_length > IN6_ADDR_MAX_CHAR_LENGTH) { - DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: too long.", + DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: too long.", (int) str_length, str)); return true; } @@ -496,7 +528,7 @@ bool Inet6::str_to_ipv6(const char *str, size_t str_length, CHARSET_INFO *cs) if (*p != ':') { - DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " + DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: " "can not start with ':x'.", (int) str_length, str)); return true; } @@ -522,7 +554,7 @@ bool Inet6::str_to_ipv6(const char *str, size_t str_length, CHARSET_INFO *cs) { if (gap_ptr) { - DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " + DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: " "too many gaps(::).", (int) str_length, str)); return true; } @@ -533,14 +565,14 @@ bool Inet6::str_to_ipv6(const char *str, size_t str_length, CHARSET_INFO *cs) if (!*p || p >= str_end) { - DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " + DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: " "ending at ':'.", (int) str_length, str)); return true; } if (dst + 2 > ipv6_bytes_end) { - DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " + DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: " "too many groups (1).", (int) str_length, str)); return true; } @@ -556,15 +588,16 @@ bool Inet6::str_to_ipv6(const char *str, size_t str_length, CHARSET_INFO *cs) { if (dst + IN_ADDR_SIZE > ipv6_bytes_end) { - DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " + DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: " "unexpected IPv4-part.", (int) str_length, str)); return true; } - Inet4_null tmp(group_start_ptr, (size_t) (str_end - group_start_ptr), cs); + Inet4_null tmp(group_start_ptr, (size_t) (str_end - group_start_ptr), + &my_charset_latin1); if (tmp.is_null()) { - DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " + DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: " "invalid IPv4-part.", (int) str_length, str)); return true; } @@ -581,7 +614,7 @@ bool Inet6::str_to_ipv6(const char *str, size_t str_length, CHARSET_INFO *cs) if (!hdp) { - DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " + DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: " "invalid character at pos %d.", (int) str_length, str, (int) (p - str))); return true; @@ -589,7 +622,7 @@ bool Inet6::str_to_ipv6(const char *str, size_t str_length, CHARSET_INFO *cs) if (chars_in_group >= 4) { - DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " + DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: " "too many digits in group.", (int) str_length, str)); return true; @@ -608,7 +641,7 @@ bool Inet6::str_to_ipv6(const char *str, size_t str_length, CHARSET_INFO *cs) { if (dst + 2 > ipv6_bytes_end) { - DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " + DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: " "too many groups (2).", (int) str_length, str)); return true; } @@ -622,7 +655,7 @@ bool Inet6::str_to_ipv6(const char *str, size_t str_length, CHARSET_INFO *cs) { if (dst == ipv6_bytes_end) { - DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " + DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: " "no room for a gap (::).", (int) str_length, str)); return true; } @@ -640,7 +673,7 @@ bool Inet6::str_to_ipv6(const char *str, size_t str_length, CHARSET_INFO *cs) if (dst < ipv6_bytes_end) { - DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: " + DBUG_PRINT("error", ("ascii_to_ipv6(%.*s): invalid IPv6 address: " "too few groups.", (int) str_length, str)); return true; }