diff options
Diffstat (limited to 'mysql-cve-2016-6663.patch')
-rw-r--r-- | mysql-cve-2016-6663.patch | 322 |
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 ¶m, 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(¶m, 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(¶m, 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(¶m, 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 ¶m, bool do_optimize) + { + optimize_done=1; + thd_proc_info(thd, "Sorting index"); +- error=mi_sort_index(¶m,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(¶m,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 + |