Skip to content

Commit 4e54738

Browse files
jhauglidnawazn
authored andcommitted
Bug#24388746: PRIVILEGE ESCALATION AND RACE CONDITION USING CREATE TABLE
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.
1 parent 684a165 commit 4e54738

File tree

6 files changed

+77
-32
lines changed

6 files changed

+77
-32
lines changed

include/my_sys.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License as published by
@@ -83,6 +83,7 @@ typedef struct my_aio_result {
8383
#define MY_RESOLVE_LINK 128 /* my_realpath(); Only resolve links */
8484
#define MY_HOLD_ORIGINAL_MODES 128 /* my_copy() holds to file modes */
8585
#define MY_REDEL_MAKE_BACKUP 256
86+
#define MY_REDEL_NO_COPY_STAT 512 /* my_redel() doesn't call my_copystat() */
8687
#define MY_SEEK_NOT_DONE 32 /* my_lock may have to do a seek */
8788
#define MY_DONT_WAIT 64 /* my_lock() don't wait if can't lock */
8889
#define MY_ZEROFILL 32 /* my_malloc(), fill array with zero */

include/myisam.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
2+
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
33
44
This program is free software; you can redistribute it and/or modify
55
it under the terms of the GNU General Public License as published by
@@ -426,12 +426,13 @@ int chk_size(MI_CHECK *param, MI_INFO *info);
426426
int chk_key(MI_CHECK *param, MI_INFO *info);
427427
int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend);
428428
int mi_repair(MI_CHECK *param, register MI_INFO *info,
429-
char * name, int rep_quick);
430-
int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name);
429+
char * name, int rep_quick, my_bool no_copy_stat);
430+
int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name,
431+
my_bool no_copy_stat);
431432
int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
432-
const char * name, int rep_quick);
433+
const char * name, int rep_quick, my_bool no_copy_stat);
433434
int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
434-
const char * name, int rep_quick);
435+
const char * name, int rep_quick, my_bool no_copy_stat);
435436
int change_to_newfile(const char * filename, const char * old_ext,
436437
const char * new_ext, myf myflags);
437438
int lock_file(MI_CHECK *param, File file, my_off_t start, int lock_type,

mysys/my_redel.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License as published by
@@ -35,6 +35,9 @@ struct utimbuf {
3535
3636
if MY_REDEL_MAKE_COPY is given, then the orginal file
3737
is renamed to org_name-'current_time'.BAK
38+
39+
if MY_REDEL_NO_COPY_STAT is given, stats are not copied
40+
from org_name to tmp_name.
3841
*/
3942

4043
#define REDEL_EXT ".BAK"
@@ -46,8 +49,11 @@ int my_redel(const char *org_name, const char *tmp_name, myf MyFlags)
4649
DBUG_PRINT("my",("org_name: '%s' tmp_name: '%s' MyFlags: %d",
4750
org_name,tmp_name,MyFlags));
4851

49-
if (my_copystat(org_name,tmp_name,MyFlags) < 0)
50-
goto end;
52+
if (!(MyFlags & MY_REDEL_NO_COPY_STAT))
53+
{
54+
if (my_copystat(org_name,tmp_name,MyFlags) < 0)
55+
goto end;
56+
}
5157
if (MyFlags & MY_REDEL_MAKE_BACKUP)
5258
{
5359
char name_buff[FN_REFLEN+20];

storage/myisam/ha_myisam.cc

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
2+
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
33
44
This program is free software; you can redistribute it and/or modify
55
it under the terms of the GNU General Public License as published by
@@ -1091,24 +1091,36 @@ int ha_myisam::repair(THD *thd, MI_CHECK &param, bool do_optimize)
10911091
/* TODO: respect myisam_repair_threads variable */
10921092
my_snprintf(buf, 40, "Repair with %d threads", my_count_bits(key_map));
10931093
thd_proc_info(thd, buf);
1094+
/*
1095+
The new file is created with the right stats, so we can skip
1096+
copying file stats from old to new.
1097+
*/
10941098
error = mi_repair_parallel(&param, file, fixed_name,
1095-
param.testflag & T_QUICK);
1099+
param.testflag & T_QUICK, TRUE);
10961100
thd_proc_info(thd, "Repair done"); // to reset proc_info, as
10971101
// it was pointing to local buffer
10981102
}
10991103
else
11001104
{
11011105
thd_proc_info(thd, "Repair by sorting");
1106+
/*
1107+
The new file is created with the right stats, so we can skip
1108+
copying file stats from old to new.
1109+
*/
11021110
error = mi_repair_by_sort(&param, file, fixed_name,
1103-
param.testflag & T_QUICK);
1111+
param.testflag & T_QUICK, TRUE);
11041112
}
11051113
}
11061114
else
11071115
{
11081116
thd_proc_info(thd, "Repair with keycache");
11091117
param.testflag &= ~T_REP_BY_SORT;
1118+
/*
1119+
The new file is created with the right stats, so we can skip
1120+
copying file stats from old to new.
1121+
*/
11101122
error= mi_repair(&param, file, fixed_name,
1111-
param.testflag & T_QUICK);
1123+
param.testflag & T_QUICK, TRUE);
11121124
}
11131125
#ifdef HAVE_MMAP
11141126
if (remap)
@@ -1124,7 +1136,11 @@ int ha_myisam::repair(THD *thd, MI_CHECK &param, bool do_optimize)
11241136
{
11251137
optimize_done=1;
11261138
thd_proc_info(thd, "Sorting index");
1127-
error=mi_sort_index(&param,file,fixed_name);
1139+
/*
1140+
The new file is created with the right stats, so we can skip
1141+
copying file stats from old to new.
1142+
*/
1143+
error=mi_sort_index(&param,file,fixed_name, TRUE);
11281144
}
11291145
if (!statistics_done && (local_testflag & T_STATISTICS))
11301146
{

storage/myisam/mi_check.c

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
2+
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
33
44
This program is free software; you can redistribute it and/or modify
55
it under the terms of the GNU General Public License as published by
@@ -1512,7 +1512,7 @@ static int mi_drop_all_indexes(MI_CHECK *param, MI_INFO *info, my_bool force)
15121512
/* Save new datafile-name in temp_filename */
15131513

15141514
int mi_repair(MI_CHECK *param, register MI_INFO *info,
1515-
char * name, int rep_quick)
1515+
char * name, int rep_quick, my_bool no_copy_stat)
15161516
{
15171517
int error,got_error;
15181518
ha_rows start_records,new_header_length;
@@ -1726,6 +1726,11 @@ int mi_repair(MI_CHECK *param, register MI_INFO *info,
17261726
/* Replace the actual file with the temporary file */
17271727
if (new_file >= 0)
17281728
{
1729+
myf flags= 0;
1730+
if (param->testflag & T_BACKUP_DATA)
1731+
flags |= MY_REDEL_MAKE_BACKUP;
1732+
if (no_copy_stat)
1733+
flags |= MY_REDEL_NO_COPY_STAT;
17291734
mysql_file_close(new_file, MYF(0));
17301735
info->dfile=new_file= -1;
17311736
/*
@@ -1744,8 +1749,7 @@ int mi_repair(MI_CHECK *param, register MI_INFO *info,
17441749
info->s->file_map= NULL;
17451750
}
17461751
if (change_to_newfile(share->data_file_name, MI_NAME_DEXT, DATA_TMP_EXT,
1747-
(param->testflag & T_BACKUP_DATA ?
1748-
MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) ||
1752+
flags) ||
17491753
mi_open_datafile(info,share,name,-1))
17501754
got_error=1;
17511755

@@ -1933,7 +1937,8 @@ int flush_blocks(MI_CHECK *param, KEY_CACHE *key_cache, File file)
19331937

19341938
/* Sort index for more efficent reads */
19351939

1936-
int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name)
1940+
int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name,
1941+
my_bool no_copy_stat)
19371942
{
19381943
reg2 uint key;
19391944
reg1 MI_KEYDEF *keyinfo;
@@ -2004,7 +2009,7 @@ int mi_sort_index(MI_CHECK *param, register MI_INFO *info, char * name)
20042009
share->kfile = -1;
20052010
(void) mysql_file_close(new_file, MYF(MY_WME));
20062011
if (change_to_newfile(share->index_file_name, MI_NAME_IEXT, INDEX_TMP_EXT,
2007-
MYF(0)) ||
2012+
no_copy_stat ? MYF(MY_REDEL_NO_COPY_STAT) : MYF(0)) ||
20082013
mi_open_keyfile(share))
20092014
goto err2;
20102015
info->lock_type= F_UNLCK; /* Force mi_readinfo to lock */
@@ -2209,14 +2214,16 @@ int filecopy(MI_CHECK *param, File to,File from,my_off_t start,
22092214
info MyISAM handler to repair
22102215
name Name of table (for warnings)
22112216
rep_quick set to <> 0 if we should not change data file
2217+
no_copy_stat Don't copy file stats from old to new file,
2218+
assume that new file was created with correct stats
22122219
22132220
RESULT
22142221
0 ok
22152222
<>0 Error
22162223
*/
22172224

22182225
int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
2219-
const char * name, int rep_quick)
2226+
const char * name, int rep_quick, my_bool no_copy_stat)
22202227
{
22212228
int got_error;
22222229
uint i;
@@ -2543,11 +2550,15 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
25432550
/* Replace the actual file with the temporary file */
25442551
if (new_file >= 0)
25452552
{
2553+
myf flags= 0;
2554+
if (param->testflag & T_BACKUP_DATA)
2555+
flags |= MY_REDEL_MAKE_BACKUP;
2556+
if (no_copy_stat)
2557+
flags |= MY_REDEL_NO_COPY_STAT;
25462558
mysql_file_close(new_file, MYF(0));
25472559
info->dfile=new_file= -1;
25482560
if (change_to_newfile(share->data_file_name,MI_NAME_DEXT, DATA_TMP_EXT,
2549-
(param->testflag & T_BACKUP_DATA ?
2550-
MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) ||
2561+
flags) ||
25512562
mi_open_datafile(info,share,name,-1))
25522563
got_error=1;
25532564
}
@@ -2595,6 +2606,8 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
25952606
info MyISAM handler to repair
25962607
name Name of table (for warnings)
25972608
rep_quick set to <> 0 if we should not change data file
2609+
no_copy_stat Don't copy file stats from old to new file,
2610+
assume that new file was created with correct stats
25982611
25992612
DESCRIPTION
26002613
Same as mi_repair_by_sort but do it multithreaded
@@ -2629,7 +2642,7 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info,
26292642
*/
26302643

26312644
int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
2632-
const char * name, int rep_quick)
2645+
const char * name, int rep_quick, my_bool no_copy_stat)
26332646
{
26342647
int got_error;
26352648
uint i,key, total_key_length, istep;
@@ -3076,11 +3089,15 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info,
30763089
/* Replace the actual file with the temporary file */
30773090
if (new_file >= 0)
30783091
{
3092+
myf flags= 0;
3093+
if (param->testflag & T_BACKUP_DATA)
3094+
flags |= MY_REDEL_MAKE_BACKUP;
3095+
if (no_copy_stat)
3096+
flags |= MY_REDEL_NO_COPY_STAT;
30793097
mysql_file_close(new_file, MYF(0));
30803098
info->dfile=new_file= -1;
30813099
if (change_to_newfile(share->data_file_name, MI_NAME_DEXT, DATA_TMP_EXT,
3082-
(param->testflag & T_BACKUP_DATA ?
3083-
MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) ||
3100+
flags) ||
30843101
mi_open_datafile(info,share,name,-1))
30853102
got_error=1;
30863103
}

storage/myisam/myisamchk.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
1+
/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
22
33
This program is free software; you can redistribute it and/or modify
44
it under the terms of the GNU General Public License as published by
@@ -993,14 +993,18 @@ static int myisamchk(MI_CHECK *param, char * filename)
993993
info->s->state.key_map,
994994
param->force_sort))
995995
{
996+
/*
997+
The new file might not be created with the right stats depending
998+
on how myisamchk is run, so we must copy file stats from old to new.
999+
*/
9961000
if (param->testflag & T_REP_BY_SORT)
997-
error=mi_repair_by_sort(param,info,filename,rep_quick);
1001+
error= mi_repair_by_sort(param, info, filename, rep_quick, FALSE);
9981002
else
999-
error=mi_repair_parallel(param,info,filename,rep_quick);
1003+
error= mi_repair_parallel(param, info, filename, rep_quick, FALSE);
10001004
state_updated=1;
10011005
}
10021006
else if (param->testflag & T_REP_ANY)
1003-
error=mi_repair(param, info,filename,rep_quick);
1007+
error= mi_repair(param, info, filename, rep_quick, FALSE);
10041008
}
10051009
if (!error && param->testflag & T_SORT_RECORDS)
10061010
{
@@ -1040,12 +1044,12 @@ static int myisamchk(MI_CHECK *param, char * filename)
10401044
{
10411045
if (param->verbose)
10421046
puts("Table had a compressed index; We must now recreate the index");
1043-
error=mi_repair_by_sort(param,info,filename,1);
1047+
error= mi_repair_by_sort(param, info, filename, 1, FALSE);
10441048
}
10451049
}
10461050
}
10471051
if (!error && param->testflag & T_SORT_INDEX)
1048-
error=mi_sort_index(param,info,filename);
1052+
error= mi_sort_index(param, info, filename, FALSE);
10491053
if (!error)
10501054
share->state.changed&= ~(STATE_CHANGED | STATE_CRASHED |
10511055
STATE_CRASHED_ON_REPAIR);

0 commit comments

Comments
 (0)