From ebb6f575689773a831bbf54d3d706a7856d88230 Mon Sep 17 00:00:00 2001
From: Aleksey Midenkov <midenok@gmail.com>
Date: Thu, 9 Nov 2023 16:26:11 +0300
Subject: [PATCH] MDEV-23294 Segfault or assertion upon MyISAM repair

When computing vcol expression some items use current_thd and that was
not set in MyISAM repair thread. Since all the repair threads belong
to one connection and items should not write into THD we can utilize
table THD for that.
---
 include/myisamchk.h                           |  1 +
 .../suite/vcol/r/vcol_keys_myisam.result      | 37 +++++++++++++++++++
 mysql-test/suite/vcol/t/vcol_keys_myisam.test | 29 +++++++++++++++
 storage/myisam/ha_myisam.cc                   | 11 ++++++
 storage/myisam/sort.c                         |  5 +++
 5 files changed, 83 insertions(+)

diff --git a/include/myisamchk.h b/include/myisamchk.h
index 5876d67ca5f..eaff2c9ffdd 100644
--- a/include/myisamchk.h
+++ b/include/myisamchk.h
@@ -117,6 +117,7 @@ typedef struct st_handler_check_param
   uint progress_counter;             /* How often to call _report_progress() */
   ulonglong progress, max_progress;
 
+  void (*init_fix_record)(void *);
   int (*fix_record)(struct st_myisam_info *info, uchar *record, int keynum);
 
   mysql_mutex_t print_msg_mutex;
diff --git a/mysql-test/suite/vcol/r/vcol_keys_myisam.result b/mysql-test/suite/vcol/r/vcol_keys_myisam.result
index cbee8b733ed..74d733948af 100644
--- a/mysql-test/suite/vcol/r/vcol_keys_myisam.result
+++ b/mysql-test/suite/vcol/r/vcol_keys_myisam.result
@@ -418,3 +418,40 @@ create or replace table t2 (pk int, b int, c int as (b) virtual, primary key (pk
 insert into t2 (pk) select a from t1;
 ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
 drop tables t1, t2;
+#
+# MDEV-23294 Segfault or assertion upon MyISAM repair
+#
+set @old_mode= @@sql_mode;
+set @old_myisam_repair_threads= @@myisam_repair_threads;
+set sql_mode='', myisam_repair_threads=2;
+create table t (a binary,b blob,c blob as (concat (a,b)),h char,index (c)) engine=innodb;
+Warnings:
+Warning	1286	Unknown storage engine 'innodb'
+Warning	1266	Using storage engine MyISAM for table 't'
+Note	1071	Specified key was too long; max key length is 1000 bytes
+insert into t values (0,0,default,0);
+create table ti like t;
+alter table ti engine=myisam;
+insert into ti select * from t;
+Warnings:
+Warning	1906	The value specified for generated column 'c' in table 'ti' has been ignored
+drop tables ti, t;
+create table t (id int,a varchar(1),b varchar(1),c varchar(1) generated always as (concat (a,b)),key(c)) engine=myisam;
+insert into t values (0,0,9687,0);
+Warnings:
+Warning	1265	Data truncated for column 'b' at row 1
+Warning	1906	The value specified for generated column 'c' in table 't' has been ignored
+Warning	1265	Data truncated for column 'c' at row 1
+repair table t quick;
+Table	Op	Msg_type	Msg_text
+test.t	repair	status	OK
+drop table t;
+create table t1 (b varchar(1024), c char(3), unique(b,c)) engine=myisam;
+insert into t1 values ('foo','baz');
+alter table t1 disable keys;
+set session myisam_repair_threads= 2;
+insert into t1 select 'qux';
+ERROR 21S01: Column count doesn't match value count at row 1
+drop table t1;
+set sql_mode= @old_mode;
+set myisam_repair_threads= @old_myisam_repair_threads;
diff --git a/mysql-test/suite/vcol/t/vcol_keys_myisam.test b/mysql-test/suite/vcol/t/vcol_keys_myisam.test
index 2a5aa3cd479..724cb8e510c 100644
--- a/mysql-test/suite/vcol/t/vcol_keys_myisam.test
+++ b/mysql-test/suite/vcol/t/vcol_keys_myisam.test
@@ -313,3 +313,32 @@ create or replace table t2 (pk int, b int, c int as (b) virtual, primary key (pk
 --error ER_DUP_ENTRY
 insert into t2 (pk) select a from t1;
 drop tables t1, t2;
+
+--echo #
+--echo # MDEV-23294 Segfault or assertion upon MyISAM repair
+--echo #
+set @old_mode= @@sql_mode;
+set @old_myisam_repair_threads= @@myisam_repair_threads;
+set sql_mode='', myisam_repair_threads=2;
+create table t (a binary,b blob,c blob as (concat (a,b)),h char,index (c)) engine=innodb;
+insert into t values (0,0,default,0);
+create table ti like t;
+alter table ti engine=myisam;
+insert into ti select * from t;
+drop tables ti, t;
+
+create table t (id int,a varchar(1),b varchar(1),c varchar(1) generated always as (concat (a,b)),key(c)) engine=myisam;
+insert into t values (0,0,9687,0);
+repair table t quick;
+drop table t;
+
+create table t1 (b varchar(1024), c char(3), unique(b,c)) engine=myisam;
+insert into t1 values ('foo','baz');
+alter table t1 disable keys;
+set session myisam_repair_threads= 2;
+--error ER_WRONG_VALUE_COUNT_ON_ROW
+insert into t1 select 'qux';
+# cleanup
+drop table t1;
+set sql_mode= @old_mode;
+set myisam_repair_threads= @old_myisam_repair_threads;
diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc
index ddab8bcf741..0db7179e7e9 100644
--- a/storage/myisam/ha_myisam.cc
+++ b/storage/myisam/ha_myisam.cc
@@ -709,6 +709,16 @@ my_bool mi_killed_in_mariadb(MI_INFO *info)
   return (((TABLE*) (info->external_ref))->in_use->killed != 0);
 }
 
+static void init_compute_vcols(void *table)
+{
+  /*
+    To evaluate vcols we must have current_thd set.
+    This will set current_thd in all threads to the same THD, but it's
+    safe, because vcols are always evaluated under info->s->intern_lock.
+  */
+  set_current_thd(static_cast<TABLE *>(table)->in_use);
+}
+
 static int compute_vcols(MI_INFO *info, uchar *record, int keynum)
 {
   /* This mutex is needed for parallel repair */
@@ -1010,6 +1020,7 @@ void ha_myisam::setup_vcols_for_repair(HA_CHECK *param)
   }
   DBUG_ASSERT(file->s->base.reclength < file->s->vreclength ||
               !table->s->stored_fields);
+  param->init_fix_record= init_compute_vcols;
   param->fix_record= compute_vcols;
   table->use_all_columns();
 }
diff --git a/storage/myisam/sort.c b/storage/myisam/sort.c
index e586543363b..f4729f75895 100644
--- a/storage/myisam/sort.c
+++ b/storage/myisam/sort.c
@@ -503,6 +503,11 @@ pthread_handler_t thr_find_all_keys(void *arg)
 {
   MI_SORT_PARAM *sort_param= (MI_SORT_PARAM*) arg;
   my_bool error= FALSE;
+
+  MI_SORT_INFO *si= sort_param->sort_info;
+  if (si->param->init_fix_record)
+    si->param->init_fix_record(si->info->external_ref);
+
   /* If my_thread_init fails */
   if (my_thread_init() || thr_find_all_keys_exec(sort_param))
     error= TRUE;