From 1d1629769c7cd20741d5b92a2387e2a0fa5fce30 Mon Sep 17 00:00:00 2001 From: Raghunandan Bhat Date: Mon, 15 Jun 2026 17:18:35 +0530 Subject: [PATCH] MDEV-39450: Memory corruption: overlapping memory ranges in `Field_longstr::compress` on UPDATE of compressed column Problem: Values shorter than `column_compression_threshold` (default 100) are stored uncompressed. Reading such a column returns a pointer into that buffer rather than a copy. When the new value is a substring of the column that aliases this buffer at a non-zero offset, e.g. `RIGHT(c,n)` or `SUBSTRING(c,n)`, the source and destination overlap, and the `memcpy()` in the "store uncompressed" path copies overlapping regions, which is undefined behaviour. Fix: Replace `memcpy` with `memmove` to avoid copying between overlapping memory regions. --- mysql-test/main/column_compression.result | 16 ++++++++++++++++ mysql-test/main/column_compression.test | 21 +++++++++++++++++++++ sql/field.cc | 2 +- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/column_compression.result b/mysql-test/main/column_compression.result index 7f176290637e9..5b35d70a24d92 100644 --- a/mysql-test/main/column_compression.result +++ b/mysql-test/main/column_compression.result @@ -3035,3 +3035,19 @@ select * from (select * from t) as t natural join (select * from t) as t1; a b drop tables t, t4; # End of 10.5 tests +# +# MDEV-39450: Memory corruption: overlapping memory ranges in `Field_longstr::compress` on UPDATE of compressed column +# +CREATE TABLE t (c VARCHAR(255) COMPRESSED); +REPLACE INTO t VALUES ('abcdefghijklm'); +UPDATE t SET c=RIGHT(c,10); +DROP TABLE t; +CREATE TABLE t (c VARCHAR(255) COMPRESSED); +REPLACE INTO t VALUES ('abcdefghijklm'); +UPDATE t SET c=SUBSTRING(c,3); +DROP TABLE t; +CREATE TABLE t (c VARCHAR(255) COMPRESSED); +REPLACE INTO t VALUES ('abcdefghijklm'); +UPDATE t SET c=MID(c,2,4); +DROP TABLE t; +# End of 10.6 tests diff --git a/mysql-test/main/column_compression.test b/mysql-test/main/column_compression.test index 874f3c3580bf2..2aba8862c6679 100644 --- a/mysql-test/main/column_compression.test +++ b/mysql-test/main/column_compression.test @@ -573,3 +573,24 @@ select * from (select * from t) as t natural join (select * from t) as t1; drop tables t, t4; --echo # End of 10.5 tests + +--echo # +--echo # MDEV-39450: Memory corruption: overlapping memory ranges in `Field_longstr::compress` on UPDATE of compressed column +--echo # + +CREATE TABLE t (c VARCHAR(255) COMPRESSED); +REPLACE INTO t VALUES ('abcdefghijklm'); +UPDATE t SET c=RIGHT(c,10); +DROP TABLE t; + +CREATE TABLE t (c VARCHAR(255) COMPRESSED); +REPLACE INTO t VALUES ('abcdefghijklm'); +UPDATE t SET c=SUBSTRING(c,3); +DROP TABLE t; + +CREATE TABLE t (c VARCHAR(255) COMPRESSED); +REPLACE INTO t VALUES ('abcdefghijklm'); +UPDATE t SET c=MID(c,2,4); +DROP TABLE t; + +--echo # End of 10.6 tests diff --git a/sql/field.cc b/sql/field.cc index 7fed7cdfc760d..4c43bb017b416 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -8543,7 +8543,7 @@ int Field_longstr::compress(char *to, uint to_length, /* Store uncompressed */ to[0]= 0; if (buf_length < to_length) - memcpy(to + 1, buf, buf_length); + memmove(to + 1, buf, buf_length); else { /* Storing string at blob capacity, e.g. 255 bytes string to TINYBLOB. */