summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.com>2019-04-30 10:53:59 +0400
committerAlexander Barkov <bar@mariadb.com>2019-04-30 10:53:59 +0400
commit5fb6444a371a5d85c832e63c02e6e8eaec2769f7 (patch)
tree2f2ec687c2cc59375e3f43012ac939dd209623d1
parent021c7216c05f3143c5113fc9a4aea3d11b6bac48 (diff)
downloadmariadb-git-5fb6444a371a5d85c832e63c02e6e8eaec2769f7.tar.gz
MDEV-18738 ASAN heap-use-after-free in copy_if_not_alloced / copy_fields
copy_if_not_alloced() did not handle situations when "from" is a constant string pointing to a substring of "to", so this code part freed "to" but then tried to copy its old (already freed) content to a new buffer: if (to->realloc(from_length)) return from; if ((to->str_length=MY_MIN(from->str_length,from_length))) memcpy(to->Ptr,from->Ptr,to->str_length); Adding a new code piece that catches such constant substrings and propery reallocs "to" to preserve its important part referenced by "from".
-rw-r--r--mysql-test/r/func_str.result27
-rw-r--r--mysql-test/t/func_str.test26
-rw-r--r--sql/sql_string.cc21
3 files changed, 74 insertions, 0 deletions
diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result
index 06cfa233f73..5f4f1378657 100644
--- a/mysql-test/r/func_str.result
+++ b/mysql-test/r/func_str.result
@@ -4757,5 +4757,32 @@ YQ== 61
Yq== 62
DROP TABLE t1;
#
+# MDEV-18738 ASAN heap-use-after-free in copy_if_not_alloced / copy_fields
+#
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1),(2);
+SELECT REPLACE( CAST( CURDATE() AS BINARY ), CURDATE(), REPEAT('a',32) ) AS f FROM t1 GROUP BY f;
+f
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+DROP TABLE t1;
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1),(2);
+SELECT REPLACE( LEFT( CURDATE(), 4), LEFT(CURDATE(),4), REPEAT('a',32) ) AS f FROM t1 GROUP BY f;
+f
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+DROP TABLE t1;
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1),(2);
+SELECT REPLACE(RIGHT(CURDATE(), 4), RIGHT(CURDATE(),4), REPEAT('a',32)) AS f FROM t1 GROUP BY f;
+f
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+DROP TABLE t1;
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1),(2);
+SELECT REPLACE(SUBSTR(CURDATE(),2,3), SUBSTR(CURDATE(),2,3), REPEAT('a',32)) AS f FROM t1 GROUP BY f;
+f
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+DROP TABLE t1;
+#
# End of 10.1 tests
#
diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test
index de954d2f60b..a6be48ad0fa 100644
--- a/mysql-test/t/func_str.test
+++ b/mysql-test/t/func_str.test
@@ -1877,6 +1877,32 @@ SELECT f1,HEX(f2) FROM t1 WHERE f1='YQ==' AND (f2= from_base64(
SELECT f1,HEX(f2) FROM t1 WHERE f1='YQ==' AND (f2= from_base64("Yq==") OR f2= from_base64("YQ=="));
DROP TABLE t1;
+
+--echo #
+--echo # MDEV-18738 ASAN heap-use-after-free in copy_if_not_alloced / copy_fields
+--echo #
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1),(2);
+SELECT REPLACE( CAST( CURDATE() AS BINARY ), CURDATE(), REPEAT('a',32) ) AS f FROM t1 GROUP BY f;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1),(2);
+SELECT REPLACE( LEFT( CURDATE(), 4), LEFT(CURDATE(),4), REPEAT('a',32) ) AS f FROM t1 GROUP BY f;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1),(2);
+SELECT REPLACE(RIGHT(CURDATE(), 4), RIGHT(CURDATE(),4), REPEAT('a',32)) AS f FROM t1 GROUP BY f;
+DROP TABLE t1;
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1),(2);
+SELECT REPLACE(SUBSTR(CURDATE(),2,3), SUBSTR(CURDATE(),2,3), REPEAT('a',32)) AS f FROM t1 GROUP BY f;
+DROP TABLE t1;
+
+
--echo #
--echo # End of 10.1 tests
--echo #
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index c22e33182c6..8c69bea2487 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -909,6 +909,27 @@ String *copy_if_not_alloced(String *to,String *from,uint32 from_length)
(void) from->realloc(from_length);
return from;
}
+ if (from->uses_buffer_owned_by(to))
+ {
+ DBUG_ASSERT(!from->alloced);
+ DBUG_ASSERT(to->alloced);
+ /*
+ "from" is a constant string pointing to a fragment of alloced string "to":
+ to= xxxFFFyyy
+ - FFF is the part of "to" pointed by "from"
+ - xxx is the part of "to" before "from"
+ - yyy is the part of "to" after "from"
+ */
+ uint32 xxx_length= (uint32) (from->ptr() - to->ptr());
+ uint32 yyy_length= (uint32) (to->end() - from->end());
+ DBUG_ASSERT(to->length() >= yyy_length);
+ to->length(to->length() - yyy_length); // Remove the "yyy" part
+ DBUG_ASSERT(to->length() >= xxx_length);
+ to->replace(0, xxx_length, "", 0); // Remove the "xxx" part
+ to->realloc(from_length);
+ to->str_charset= from->str_charset;
+ return to;
+ }
if (to->realloc(from_length))
return from; // Actually an error
if ((to->str_length=MY_MIN(from->str_length,from_length)))