From a11e9e7e9c58d25eff05ab46aa3f74d24d5f784a Mon Sep 17 00:00:00 2001 From: Dave Gosselin Date: Thu, 1 Oct 2026 15:08:57 -0400 Subject: [PATCH] MDEV-24907: Empty the source list after merging an Item_equal With save_merged false, Item_equal::merge_into_list() merges this object's Item_equal instances into another, linking *this object's items into the target. However this allowed callers to free an item via one list but later read it through the other; a case of use-after-free. If save_merged is false then delete the items from the source. --- mysql-test/main/subselect_mat.result | 24 ++++++++++++++++++++++ mysql-test/main/subselect_sj_mat.result | 24 ++++++++++++++++++++++ mysql-test/main/subselect_sj_mat.test | 27 +++++++++++++++++++++++++ sql/item_cmpfunc.cc | 10 +++++++++ 4 files changed, 85 insertions(+) diff --git a/mysql-test/main/subselect_mat.result b/mysql-test/main/subselect_mat.result index b048dc54ab6..01fe66beb71 100644 --- a/mysql-test/main/subselect_mat.result +++ b/mysql-test/main/subselect_mat.result @@ -2754,6 +2754,30 @@ SELECT * FROM t1 WHERE (0,0) IN (SELECT MAX(c),MIN(c) FROM t1); c 0 DROP TABLE t1; +# +# MDEV-24907: Empty the source list after merging an Item_equal +# +CREATE TABLE t1 (a INT, b INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (0,0); +CREATE TABLE t2 (c INT) ENGINE=MyISAM; +INSERT INTO t2 VALUES (2),(0); +CREATE TABLE t3 (d INT) ENGINE=MyISAM; +INSERT INTO t3 VALUES (1),(3); +SELECT * FROM t1 LEFT JOIN ( +t2 JOIN t3 ON (t3.d = t2.c) +) ON ( +t1.b NOT IN (SELECT d FROM t3) +) WHERE (t1.a, t3.d) IN (SELECT c, MAX(c) FROM t2) AND t1.a = t1.b; +a b c d +SELECT * FROM t1 LEFT JOIN ( +t2 JOIN t3 ON (t3.d = t2.c) +) ON ( +t1.b > 3 +) WHERE t1.a IN (SELECT MAX(c) FROM t2 GROUP BY c) AND +t3.d IN (SELECT COUNT(c) FROM t2 GROUP BY c) AND +t1.a = t1.b; +a b c d +DROP TABLE t1, t2, t3; # end of 10.11 tests set @subselect_mat_test_optimizer_switch_value=null; set @@optimizer_switch='materialization=on,in_to_exists=off,semijoin=off'; diff --git a/mysql-test/main/subselect_sj_mat.result b/mysql-test/main/subselect_sj_mat.result index 3e30d3828e5..08c5fdc1a46 100644 --- a/mysql-test/main/subselect_sj_mat.result +++ b/mysql-test/main/subselect_sj_mat.result @@ -2796,4 +2796,28 @@ SELECT * FROM t1 WHERE (0,0) IN (SELECT MAX(c),MIN(c) FROM t1); c 0 DROP TABLE t1; +# +# MDEV-24907: Empty the source list after merging an Item_equal +# +CREATE TABLE t1 (a INT, b INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (0,0); +CREATE TABLE t2 (c INT) ENGINE=MyISAM; +INSERT INTO t2 VALUES (2),(0); +CREATE TABLE t3 (d INT) ENGINE=MyISAM; +INSERT INTO t3 VALUES (1),(3); +SELECT * FROM t1 LEFT JOIN ( +t2 JOIN t3 ON (t3.d = t2.c) +) ON ( +t1.b NOT IN (SELECT d FROM t3) +) WHERE (t1.a, t3.d) IN (SELECT c, MAX(c) FROM t2) AND t1.a = t1.b; +a b c d +SELECT * FROM t1 LEFT JOIN ( +t2 JOIN t3 ON (t3.d = t2.c) +) ON ( +t1.b > 3 +) WHERE t1.a IN (SELECT MAX(c) FROM t2 GROUP BY c) AND +t3.d IN (SELECT COUNT(c) FROM t2 GROUP BY c) AND +t1.a = t1.b; +a b c d +DROP TABLE t1, t2, t3; # end of 10.11 tests diff --git a/mysql-test/main/subselect_sj_mat.test b/mysql-test/main/subselect_sj_mat.test index 5bbd9d29b96..e7aa6dbbaca 100644 --- a/mysql-test/main/subselect_sj_mat.test +++ b/mysql-test/main/subselect_sj_mat.test @@ -2472,4 +2472,31 @@ SELECT * FROM t1 WHERE (0,0) IN (SELECT MAX(c),MIN(c) FROM t1); DROP TABLE t1; +--echo # +--echo # MDEV-24907: Empty the source list after merging an Item_equal +--echo # +CREATE TABLE t1 (a INT, b INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (0,0); +CREATE TABLE t2 (c INT) ENGINE=MyISAM; +INSERT INTO t2 VALUES (2),(0); +CREATE TABLE t3 (d INT) ENGINE=MyISAM; +INSERT INTO t3 VALUES (1),(3); + +# Result should be empty. +SELECT * FROM t1 LEFT JOIN ( + t2 JOIN t3 ON (t3.d = t2.c) +) ON ( + t1.b NOT IN (SELECT d FROM t3) +) WHERE (t1.a, t3.d) IN (SELECT c, MAX(c) FROM t2) AND t1.a = t1.b; + +SELECT * FROM t1 LEFT JOIN ( + t2 JOIN t3 ON (t3.d = t2.c) +) ON ( + t1.b > 3 +) WHERE t1.a IN (SELECT MAX(c) FROM t2 GROUP BY c) AND + t3.d IN (SELECT COUNT(c) FROM t2 GROUP BY c) AND + t1.a = t1.b; + +DROP TABLE t1, t2, t3; + --echo # end of 10.11 tests diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 0deee4d885c..7c78c61216b 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -7233,7 +7233,17 @@ void Item_equal::merge_into_list(THD *thd, List *list, if (!merge_into) { if (item->merge_with_check(thd, this, save_merged)) + { merge_into= item; + /* + If the caller doesn't set save_merged, then remove the items from the + source, which is 'this' object. That prevents an ASAN problem where + an item appearing in both lists has its memory deleted via one list + and then is later read via the other. + */ + if (!save_merged) + equal_items.empty(); + } } else {