Commit 28f4090b authored by Staale Smedseng's avatar Staale Smedseng

Bug #42502 huge memory leak possible with timezone

functions
      
Unknown timezone specifications are properly rejected
by the server, but are copied into tz_storage before
rejection, and hence is retained until end of server 
life. With sufficiently large bogus timezone specs, 
it is easy to exhaust system memory.
      
Allocation of memory for a copy of the timezone
name is delayed until after verification of validity,
at the cost of a memcpy of the timezone info. This 
only happens once, future lookups will hit the cached
structure.
parent a4f59d4a
...@@ -1871,6 +1871,12 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) ...@@ -1871,6 +1871,12 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
#ifdef ABBR_ARE_USED #ifdef ABBR_ARE_USED
char chars[max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))]; char chars[max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))];
#endif #endif
/*
Used as a temporary tz_info until we decide that we actually want to
allocate and keep the tz info and tz name in tz_storage.
*/
TIME_ZONE_INFO tmp_tz_info;
memset(&tmp_tz_info, 0, sizeof(TIME_ZONE_INFO));
DBUG_ENTER("tz_load_from_open_tables"); DBUG_ENTER("tz_load_from_open_tables");
...@@ -1914,7 +1920,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) ...@@ -1914,7 +1920,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
Most probably user has mistyped time zone name, so no need to bark here Most probably user has mistyped time zone name, so no need to bark here
unless we need it for debugging. unless we need it for debugging.
*/ */
sql_print_error("Can't find description of time zone '%s'", tz_name_buff); sql_print_error("Can't find description of time zone '%.*s'",
tz_name->length(), tz_name->ptr());
#endif #endif
goto end; goto end;
} }
...@@ -1943,8 +1950,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) ...@@ -1943,8 +1950,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
/* If Uses_leap_seconds == 'Y' */ /* If Uses_leap_seconds == 'Y' */
if (table->field[1]->val_int() == 1) if (table->field[1]->val_int() == 1)
{ {
tz_info->leapcnt= tz_leapcnt; tmp_tz_info.leapcnt= tz_leapcnt;
tz_info->lsis= tz_lsis; tmp_tz_info.lsis= tz_lsis;
} }
(void)table->file->ha_index_end(); (void)table->file->ha_index_end();
...@@ -1981,18 +1988,18 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) ...@@ -1981,18 +1988,18 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
#ifdef ABBR_ARE_USED #ifdef ABBR_ARE_USED
// FIXME should we do something with duplicates here ? // FIXME should we do something with duplicates here ?
table->field[4]->val_str(&abbr, &abbr); table->field[4]->val_str(&abbr, &abbr);
if (tz_info->charcnt + abbr.length() + 1 > sizeof(chars)) if (tmp_tz_info.charcnt + abbr.length() + 1 > sizeof(chars))
{ {
sql_print_error("Error while loading time zone description from " sql_print_error("Error while loading time zone description from "
"mysql.time_zone_transition_type table: not enough " "mysql.time_zone_transition_type table: not enough "
"room for abbreviations"); "room for abbreviations");
goto end; goto end;
} }
ttis[ttid].tt_abbrind= tz_info->charcnt; ttis[ttid].tt_abbrind= tmp_tz_info.charcnt;
memcpy(chars + tz_info->charcnt, abbr.ptr(), abbr.length()); memcpy(chars + tmp_tz_info.charcnt, abbr.ptr(), abbr.length());
tz_info->charcnt+= abbr.length(); tmp_tz_info.charcnt+= abbr.length();
chars[tz_info->charcnt]= 0; chars[tmp_tz_info.charcnt]= 0;
tz_info->charcnt++; tmp_tz_info.charcnt++;
DBUG_PRINT("info", DBUG_PRINT("info",
("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld " ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
...@@ -2005,9 +2012,9 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) ...@@ -2005,9 +2012,9 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
#endif #endif
/* ttid is increasing because we are reading using index */ /* ttid is increasing because we are reading using index */
DBUG_ASSERT(ttid >= tz_info->typecnt); DBUG_ASSERT(ttid >= tmp_tz_info.typecnt);
tz_info->typecnt= ttid + 1; tmp_tz_info.typecnt= ttid + 1;
res= table->file->index_next_same(table->record[0], res= table->file->index_next_same(table->record[0],
(byte*)table->field[0]->ptr, 4); (byte*)table->field[0]->ptr, 4);
...@@ -2040,14 +2047,14 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) ...@@ -2040,14 +2047,14 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
ttime= (my_time_t)table->field[1]->val_int(); ttime= (my_time_t)table->field[1]->val_int();
ttid= (uint)table->field[2]->val_int(); ttid= (uint)table->field[2]->val_int();
if (tz_info->timecnt + 1 > TZ_MAX_TIMES) if (tmp_tz_info.timecnt + 1 > TZ_MAX_TIMES)
{ {
sql_print_error("Error while loading time zone description from " sql_print_error("Error while loading time zone description from "
"mysql.time_zone_transition table: " "mysql.time_zone_transition table: "
"too much transitions"); "too much transitions");
goto end; goto end;
} }
if (ttid + 1 > tz_info->typecnt) if (ttid + 1 > tmp_tz_info.typecnt)
{ {
sql_print_error("Error while loading time zone description from " sql_print_error("Error while loading time zone description from "
"mysql.time_zone_transition table: " "mysql.time_zone_transition table: "
...@@ -2055,9 +2062,9 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) ...@@ -2055,9 +2062,9 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
goto end; goto end;
} }
ats[tz_info->timecnt]= ttime; ats[tmp_tz_info.timecnt]= ttime;
types[tz_info->timecnt]= ttid; types[tmp_tz_info.timecnt]= ttid;
tz_info->timecnt++; tmp_tz_info.timecnt++;
DBUG_PRINT("info", DBUG_PRINT("info",
("time_zone_transition table: tz_id: %u tt_time: %lu tt_id: %u", ("time_zone_transition table: tz_id: %u tt_time: %lu tt_id: %u",
...@@ -2081,6 +2088,34 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) ...@@ -2081,6 +2088,34 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
(void)table->file->ha_index_end(); (void)table->file->ha_index_end();
table= 0; table= 0;
/*
Let us check how correct our time zone description is. We don't check for
tz->timecnt < 1 since it is ok for GMT.
*/
if (tmp_tz_info.typecnt < 1)
{
sql_print_error("loading time zone without transition types");
goto end;
}
/* Allocate memory for the timezone info and timezone name in tz_storage. */
if (!(alloc_buff= (char*) alloc_root(&tz_storage, sizeof(TIME_ZONE_INFO) +
tz_name->length() + 1)))
{
sql_print_error("Out of memory while loading time zone description");
return 0;
}
/* Move the temporary tz_info into the allocated area */
tz_info= (TIME_ZONE_INFO *)alloc_buff;
memcpy(tz_info, &tmp_tz_info, sizeof(TIME_ZONE_INFO));
tz_name_buff= alloc_buff + sizeof(TIME_ZONE_INFO);
/*
By writing zero to the end we guarantee that we can call ptr()
instead of c_ptr() for time zone name.
*/
strmake(tz_name_buff, tz_name->ptr(), tz_name->length());
/* /*
Now we will allocate memory and init TIME_ZONE_INFO structure. Now we will allocate memory and init TIME_ZONE_INFO structure.
*/ */
...@@ -2112,15 +2147,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) ...@@ -2112,15 +2147,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
tz_info->ttis= (TRAN_TYPE_INFO *)alloc_buff; tz_info->ttis= (TRAN_TYPE_INFO *)alloc_buff;
memcpy(tz_info->ttis, ttis, tz_info->typecnt * sizeof(TRAN_TYPE_INFO)); memcpy(tz_info->ttis, ttis, tz_info->typecnt * sizeof(TRAN_TYPE_INFO));
/* /* Build reversed map. */
Let us check how correct our time zone description and build
reversed map. We don't check for tz->timecnt < 1 since it ok for GMT.
*/
if (tz_info->typecnt < 1)
{
sql_print_error("loading time zone without transition types");
goto end;
}
if (prepare_tz_info(tz_info, &tz_storage)) if (prepare_tz_info(tz_info, &tz_storage))
{ {
sql_print_error("Unable to build mktime map for time zone"); sql_print_error("Unable to build mktime map for time zone");
......
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