Commit 691e964d authored by Sergei Petrunia's avatar Sergei Petrunia

MDEV-31764: ASAN use-after-poison in trace_engine_stats in ANALYZE JSON

Do not attempt to produce "r_engine_stats" on the temporary (=work) tables.
These tables may be
- re-created during the query execution
- freed during the query execution (This is done e.g. in JOIN::cleanup(),
  before we produce ANALYZE FORMAT=JSON output).

- (Also, make save_explain_data() functions not set handler_for_stats
  to point to handler objects that do not have handler->handler_stats set.
  If the storage engine is not collecting handler_stats, it will not have
  them when we're producing ANALYZE FORMAT=JSON output, either).
parent 138717b1
......@@ -103,3 +103,70 @@ select cast(json_extract(@out,'$[0]') as DOUBLE) > 0 as PAGES_UPDATED_MORE_THAN_
PAGES_UPDATED_MORE_THAN_ZERO
1
drop table t1;
#
# MDEV-31764: ASAN use-after-poison in trace_engine_stats upon ANALYZE FORMAT=JSON
#
ANALYZE FORMAT=JSON SELECT count(*) FROM information_schema.GLOBAL_STATUS;
# Another testcase without I_S:
CREATE TABLE t1 (a INT);
INSERT INTO t1 SELECT seq FROM seq_1_to_100;
CREATE TABLE t2 (s INT);
INSERT INTO t2 SELECT seq FROM seq_1_to_10;
# Must use SJ-Materialization to hit the issue with temp.table:
ANALYZE FORMAT=JSON SELECT * FROM t1 WHERE a IN (SELECT s FROM t2);
ANALYZE
{
"query_block": {
"select_id": 1,
"r_loops": 1,
"r_total_time_ms": "REPLACED",
"const_condition": "1",
"table": {
"table_name": "t1",
"access_type": "ALL",
"r_loops": 1,
"rows": 100,
"r_rows": 100,
"r_table_time_ms": "REPLACED",
"r_other_time_ms": "REPLACED",
"r_engine_stats": REPLACED,
"filtered": 100,
"r_filtered": 100
},
"table": {
"table_name": "<subquery2>",
"access_type": "eq_ref",
"possible_keys": ["distinct_key"],
"key": "distinct_key",
"key_length": "4",
"used_key_parts": ["s"],
"ref": ["func"],
"r_loops": 100,
"rows": 1,
"r_rows": 0.1,
"r_table_time_ms": "REPLACED",
"r_other_time_ms": "REPLACED",
"filtered": 100,
"r_filtered": 100,
"materialized": {
"unique": 1,
"query_block": {
"select_id": 2,
"table": {
"table_name": "t2",
"access_type": "ALL",
"r_loops": 1,
"rows": 10,
"r_rows": 10,
"r_table_time_ms": "REPLACED",
"r_other_time_ms": "REPLACED",
"r_engine_stats": REPLACED,
"filtered": 100,
"r_filtered": 100
}
}
}
}
}
}
DROP TABLE t1, t2;
......@@ -62,3 +62,23 @@ select cast(json_extract(@out,'$[0]') as DOUBLE) > 0 as PAGES_UPDATED_MORE_THAN_
drop table t1;
--echo #
--echo # MDEV-31764: ASAN use-after-poison in trace_engine_stats upon ANALYZE FORMAT=JSON
--echo #
--disable_result_log
ANALYZE FORMAT=JSON SELECT count(*) FROM information_schema.GLOBAL_STATUS;
--enable_result_log
--echo # Another testcase without I_S:
CREATE TABLE t1 (a INT);
INSERT INTO t1 SELECT seq FROM seq_1_to_100;
CREATE TABLE t2 (s INT);
INSERT INTO t2 SELECT seq FROM seq_1_to_10;
--echo # Must use SJ-Materialization to hit the issue with temp.table:
--source include/analyze-format.inc
ANALYZE FORMAT=JSON SELECT * FROM t1 WHERE a IN (SELECT s FROM t2);
DROP TABLE t1, t2;
......@@ -124,9 +124,12 @@ bool Update_plan::save_explain_data_intern(THD *thd,
if (is_analyze ||
(thd->variables.log_slow_verbosity &
LOG_SLOW_VERBOSITY_ENGINE))
{
table->file->set_time_tracker(&explain->table_tracker);
explain->handler_for_stats= table->file;
if (table->file->handler_stats && table->s->tmp_table != INTERNAL_TMP_TABLE)
explain->handler_for_stats= table->file;
}
select_lex->set_explain_type(TRUE);
explain->select_type= select_lex->type;
......
......@@ -845,12 +845,18 @@ class Explain_table_access : public Sql_alloc
Gap_time_tracker extra_time_tracker;
/*
Note: This pointer is only valid until notify_tables_are_closed() is
called. After that, the tables may be freed or reused, together with their
handler_stats objects.
Handler object to get the handler_stats from.
Notes:
This pointer is only valid until notify_tables_are_closed() is called.
After that, the tables may be freed or reused, together with their
handler_stats objects.
notify_tables_are_closed() disables printing of FORMAT=JSON output.
r_engine_stats is only printed in FORMAT=JSON output, so we're fine.
We do not store pointers to temporary (aka "work") tables here.
Temporary tables may be freed (e.g. by JOIN::cleanup()) or re-created
during query execution (when HEAP table is converted into Aria).
*/
handler *handler_for_stats;
......
......@@ -27755,12 +27755,16 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta,
LOG_SLOW_VERBOSITY_ENGINE))
{
table->file->set_time_tracker(&eta->op_tracker);
/*
Set handler_for_stats even if we are not running an ANALYZE command.
There's no harm, and in case somebody runs a SHOW ANALYZE command we'll
be able to print the engine statistics.
*/
eta->handler_for_stats= table->file;
if (table->file->handler_stats &&
table->s->tmp_table != INTERNAL_TMP_TABLE)
eta->handler_for_stats= table->file;
if (likely(thd->lex->analyze_stmt))
{
eta->op_tracker.set_gap_tracker(&eta->extra_time_tracker);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment