summaryrefslogtreecommitdiffstats
path: root/mysql-cve-2016-6663.patch
diff options
context:
space:
mode:
Diffstat (limited to 'mysql-cve-2016-6663.patch')
-rw-r--r--mysql-cve-2016-6663.patch322
1 files changed, 322 insertions, 0 deletions
diff --git a/mysql-cve-2016-6663.patch b/mysql-cve-2016-6663.patch
new file mode 100644
index 0000000..760d980
--- /dev/null
+++ b/mysql-cve-2016-6663.patch
@@ -0,0 +1,322 @@
+From b66f369e343e3b7c4920512d6eac427f23ae3b89 Mon Sep 17 00:00:00 2001
+From: Honza Horak <hhorak@redhat.com>
+Date: Thu, 8 Dec 2016 10:31:58 +0100
+Subject: [PATCH] Bug#24388746: PRIVILEGE ESCALATION AND RACE CONDITION USING
+ CREATE TABLE
+
+MySQL 5.1 upstream backport of:
+https://github.com/mysql/mysql-server/commit/4e5473862e6852b0f3802b0cd0c6fa10b5253291
+
+During REPAIR TABLE of a MyISAM table, a temporary data file (.TMD)
+is created. When repair finishes, this file is renamed to the original
+.MYD file. The problem was that during this rename, we copied the
+stats from the old file to the new file with chmod/chown. If a user
+managed to replace the temporary file before chmod/chown was executed,
+it was possible to get an arbitrary file with the privileges of the
+mysql user.
+
+This patch fixes the problem by not copying stats from the old
+file to the new file. This is not needed as the new file was
+created with the correct stats. This fix only changes server
+behavior - external utilities such as myisamchk still does
+chmod/chown.
+
+No test case provided since the problem involves synchronization
+with file system operations.
+---
+ include/my_sys.h | 1 +
+ include/myisam.h | 9 +++++----
+ mysys/my_redel.c | 10 ++++++++--
+ storage/myisam/ha_myisam.cc | 24 ++++++++++++++++++++----
+ storage/myisam/mi_check.c | 39 ++++++++++++++++++++++++++++-----------
+ storage/myisam/myisamchk.c | 14 +++++++++-----
+ 6 files changed, 71 insertions(+), 26 deletions(-)
+
+diff --git a/include/my_sys.h b/include/my_sys.h
+index a2742e4..2620168 100644
+--- a/include/my_sys.h
++++ b/include/my_sys.h
+@@ -72,6 +72,7 @@ extern int NEAR my_errno; /* Last error in mysys */
+ #define MY_RESOLVE_LINK 128 /* my_realpath(); Only resolve links */
+ #define MY_HOLD_ORIGINAL_MODES 128 /* my_copy() holds to file modes */
+ #define MY_REDEL_MAKE_BACKUP 256
++#define MY_REDEL_NO_COPY_STAT 512 /* my_redel() doesn't call my_copystat() */
+ #define MY_SEEK_NOT_DONE 32 /* my_lock may have to do a seek */
+ #define MY_DONT_WAIT 64 /* my_lock() don't wait if can't lock */
+ #define MY_ZEROFILL 32 /* my_malloc(), fill array with zero */
+diff --git a/include/myisam.h b/include/myisam.h
+index 92e6699..5ae177c 100644
+--- a/include/myisam.h
++++ b/include/myisam.h
+@@ -498,12 +498,13 @@ int chk_size(MI_CHECK *param, MI_INFO *info);
+ int chk_key(MI_CHECK *param, MI_INFO *info);
+ int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend);
+ int mi_repair(MI_CHECK *param, register MI_INFO *info,
+- char * name, int rep_quick);
+-int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name);
++ char * name, int rep_quick, my_bool no_copy_stat);
++int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name,
++ my_bool no_copy_stat);
+ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
+- const char * name, int rep_quick);
++ const char * name, int rep_quick, my_bool no_copy_stat);
+ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
+- const char * name, int rep_quick);
++ const char * name, int rep_quick, my_bool no_copy_stat);
+ int change_to_newfile(const char * filename, const char * old_ext,
+ const char * new_ext, uint raid_chunks,
+ myf myflags);
+diff --git a/mysys/my_redel.c b/mysys/my_redel.c
+index f8d43d2..0859696 100644
+--- a/mysys/my_redel.c
++++ b/mysys/my_redel.c
+@@ -37,6 +37,9 @@ struct utimbuf {
+
+ if MY_REDEL_MAKE_COPY is given, then the orginal file
+ is renamed to org_name-'current_time'.BAK
++
++ if MY_REDEL_NO_COPY_STAT is given, stats are not copied
++ from org_name to tmp_name.
+ */
+
+ #define REDEL_EXT ".BAK"
+@@ -48,8 +51,11 @@ int my_redel(const char *org_name, const char *tmp_name, myf MyFlags)
+ DBUG_PRINT("my",("org_name: '%s' tmp_name: '%s' MyFlags: %d",
+ org_name,tmp_name,MyFlags));
+
+- if (my_copystat(org_name,tmp_name,MyFlags) < 0)
+- goto end;
++ if (!(MyFlags & MY_REDEL_NO_COPY_STAT))
++ {
++ if (my_copystat(org_name,tmp_name,MyFlags) < 0)
++ goto end;
++ }
+ if (MyFlags & MY_REDEL_MAKE_BACKUP)
+ {
+ char name_buff[FN_REFLEN+20];
+diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc
+index 173dc35..9a561f5 100644
+--- a/storage/myisam/ha_myisam.cc
++++ b/storage/myisam/ha_myisam.cc
+@@ -1159,24 +1159,36 @@ int ha_myisam::repair(THD *thd, MI_CHECK &param, bool do_optimize)
+ /* TODO: respect myisam_repair_threads variable */
+ my_snprintf(buf, 40, "Repair with %d threads", my_count_bits(key_map));
+ thd_proc_info(thd, buf);
++ /*
++ * The new file is created with the right stats, so we can skip
++ * copying file stats from old to new.
++ */
+ error = mi_repair_parallel(&param, file, fixed_name,
+- param.testflag & T_QUICK);
++ param.testflag & T_QUICK, TRUE);
+ thd_proc_info(thd, "Repair done"); // to reset proc_info, as
+ // it was pointing to local buffer
+ }
+ else
+ {
+ thd_proc_info(thd, "Repair by sorting");
++ /*
++ * The new file is created with the right stats, so we can skip
++ * copying file stats from old to new.
++ */
+ error = mi_repair_by_sort(&param, file, fixed_name,
+- param.testflag & T_QUICK);
++ param.testflag & T_QUICK, TRUE);
+ }
+ }
+ else
+ {
+ thd_proc_info(thd, "Repair with keycache");
+ param.testflag &= ~T_REP_BY_SORT;
++ /*
++ * The new file is created with the right stats, so we can skip
++ * copying file stats from old to new.
++ */
+ error= mi_repair(&param, file, fixed_name,
+- param.testflag & T_QUICK);
++ param.testflag & T_QUICK, TRUE);
+ }
+ #ifdef HAVE_MMAP
+ if (remap)
+@@ -1192,7 +1204,11 @@ int ha_myisam::repair(THD *thd, MI_CHECK &param, bool do_optimize)
+ {
+ optimize_done=1;
+ thd_proc_info(thd, "Sorting index");
+- error=mi_sort_index(&param,file,fixed_name);
++ /*
++ * The new file is created with the right stats, so we can skip
++ * copying file stats from old to new.
++ */
++ error=mi_sort_index(&param,file,fixed_name, TRUE);
+ }
+ if (!statistics_done && (local_testflag & T_STATISTICS))
+ {
+diff --git a/storage/myisam/mi_check.c b/storage/myisam/mi_check.c
+index 698c1f3..b071061 100644
+--- a/storage/myisam/mi_check.c
++++ b/storage/myisam/mi_check.c
+@@ -1520,7 +1520,7 @@ static int mi_drop_all_indexes(MI_CHECK *param, MI_INFO *info, my_bool force)
+ /* Save new datafile-name in temp_filename */
+
+ int mi_repair(MI_CHECK *param, register MI_INFO *info,
+- char * name, int rep_quick)
++ char * name, int rep_quick, my_bool no_copy_stat)
+ {
+ int error,got_error;
+ ha_rows start_records,new_header_length;
+@@ -1736,12 +1736,16 @@ err:
+ /* Replace the actual file with the temporary file */
+ if (new_file >= 0)
+ {
++ myf flags= 0;
++ if (param->testflag & T_BACKUP_DATA)
++ flags |= MY_REDEL_MAKE_BACKUP;
++ if (no_copy_stat)
++ flags |= MY_REDEL_NO_COPY_STAT;
+ my_close(new_file,MYF(0));
+ info->dfile=new_file= -1;
+ if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
+ DATA_TMP_EXT, share->base.raid_chunks,
+- (param->testflag & T_BACKUP_DATA ?
+- MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) ||
++ flags) ||
+ mi_open_datafile(info,share,name,-1))
+ got_error=1;
+
+@@ -1931,7 +1935,8 @@ int flush_blocks(MI_CHECK *param, KEY_CACHE *key_cache, File file)
+
+ /* Sort index for more efficent reads */
+
+-int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name)
++int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name,
++ my_bool no_copy_stat)
+ {
+ reg2 uint key;
+ reg1 MI_KEYDEF *keyinfo;
+@@ -2000,7 +2005,7 @@ int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name)
+ share->kfile = -1;
+ VOID(my_close(new_file,MYF(MY_WME)));
+ if (change_to_newfile(share->index_file_name,MI_NAME_IEXT,INDEX_TMP_EXT,0,
+- MYF(0)) ||
++ no_copy_stat ? MYF(MY_REDEL_NO_COPY_STAT) : MYF(0)) ||
+ mi_open_keyfile(share))
+ goto err2;
+ info->lock_type= F_UNLCK; /* Force mi_readinfo to lock */
+@@ -2213,6 +2218,8 @@ err:
+ info MyISAM handler to repair
+ name Name of table (for warnings)
+ rep_quick set to <> 0 if we should not change data file
++ no_copy_stat Don't copy file stats from old to new file,
++ assume that new file was created with correct stats
+
+ RESULT
+ 0 ok
+@@ -2220,7 +2227,7 @@ err:
+ */
+
+ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
+- const char * name, int rep_quick)
++ const char * name, int rep_quick, my_bool no_copy_stat)
+ {
+ int got_error;
+ uint i;
+@@ -2549,12 +2556,16 @@ err:
+ /* Replace the actual file with the temporary file */
+ if (new_file >= 0)
+ {
++ myf flags= 0;
++ if (param->testflag & T_BACKUP_DATA)
++ flags |= MY_REDEL_MAKE_BACKUP;
++ if (no_copy_stat)
++ flags |= MY_REDEL_NO_COPY_STAT;
+ my_close(new_file,MYF(0));
+ info->dfile=new_file= -1;
+ if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
+ DATA_TMP_EXT, share->base.raid_chunks,
+- (param->testflag & T_BACKUP_DATA ?
+- MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) ||
++ flags) ||
+ mi_open_datafile(info,share,name,-1))
+ got_error=1;
+ }
+@@ -2604,6 +2615,8 @@ err:
+ info MyISAM handler to repair
+ name Name of table (for warnings)
+ rep_quick set to <> 0 if we should not change data file
++ no_copy_stat Don't copy file stats from old to new file,
++ assume that new file was created with correct stats
+
+ DESCRIPTION
+ Same as mi_repair_by_sort but do it multithreaded
+@@ -2638,7 +2651,7 @@ err:
+ */
+
+ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
+- const char * name, int rep_quick)
++ const char * name, int rep_quick, my_bool no_copy_stat)
+ {
+ #ifndef THREAD
+ return mi_repair_by_sort(param, info, name, rep_quick);
+@@ -3086,12 +3099,16 @@ err:
+ /* Replace the actual file with the temporary file */
+ if (new_file >= 0)
+ {
++ myf flags= 0;
++ if (param->testflag & T_BACKUP_DATA)
++ flags |= MY_REDEL_MAKE_BACKUP;
++ if (no_copy_stat)
++ flags |= MY_REDEL_NO_COPY_STAT;
+ my_close(new_file,MYF(0));
+ info->dfile=new_file= -1;
+ if (change_to_newfile(share->data_file_name,MI_NAME_DEXT,
+ DATA_TMP_EXT, share->base.raid_chunks,
+- (param->testflag & T_BACKUP_DATA ?
+- MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) ||
++ flags) ||
+ mi_open_datafile(info,share,name,-1))
+ got_error=1;
+ }
+diff --git a/storage/myisam/myisamchk.c b/storage/myisam/myisamchk.c
+index 282a02a..bd14c34 100644
+--- a/storage/myisam/myisamchk.c
++++ b/storage/myisam/myisamchk.c
+@@ -1020,14 +1020,18 @@ static int myisamchk(MI_CHECK *param, char * filename)
+ info->s->state.key_map,
+ param->force_sort))
+ {
++ /*
++ The new file might not be created with the right stats depending
++ on how myisamchk is run, so we must copy file stats from old to new.
++ */
+ if (param->testflag & T_REP_BY_SORT)
+- error=mi_repair_by_sort(param,info,filename,rep_quick);
++ error=mi_repair_by_sort(param,info,filename,rep_quick,FALSE);
+ else
+- error=mi_repair_parallel(param,info,filename,rep_quick);
++ error=mi_repair_parallel(param,info,filename,rep_quick,FALSE);
+ state_updated=1;
+ }
+ else if (param->testflag & T_REP_ANY)
+- error=mi_repair(param, info,filename,rep_quick);
++ error=mi_repair(param, info,filename,rep_quick,FALSE);
+ }
+ if (!error && param->testflag & T_SORT_RECORDS)
+ {
+@@ -1069,12 +1073,12 @@ static int myisamchk(MI_CHECK *param, char * filename)
+ {
+ if (param->verbose)
+ puts("Table had a compressed index; We must now recreate the index");
+- error=mi_repair_by_sort(param,info,filename,1);
++ error=mi_repair_by_sort(param,info,filename,1,FALSE);
+ }
+ }
+ }
+ if (!error && param->testflag & T_SORT_INDEX)
+- error=mi_sort_index(param,info,filename);
++ error=mi_sort_index(param,info,filename,FALSE);
+ if (!error)
+ share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
+ STATE_CRASHED_ON_REPAIR);
+--
+2.7.4
+