Commit c2ebb0ac authored by Alexey Kopytov's avatar Alexey Kopytov

Bug #42064: low memory crash when importing hex strings, in

            Item_hex_string::Item_hex_string

The status of memory allocation in the Lex_input_stream (called
from the Parser_state constructor) was not checked which led to
a parser crash in case of the out-of-memory error.

The solution is to introduce new init() member function in
Parser_state and Lex_input_stream so that status of memory
allocation can be returned to the caller.

mysql-test/r/error_simulation.result:
  Added a test case for bug #42064.
mysql-test/t/error_simulation.test:
  Added a test case for bug #42064.
mysys/my_alloc.c:
  Added error injection code for the regression test.
mysys/my_malloc.c:
  Added error injection code for the regression test.
mysys/safemalloc.c:
  Added error injection code for the regression test.
sql/event_data_objects.cc:
  Use the new init() member function of Parser_state and check
  its return value to handle memory allocation failures.
sql/mysqld.cc:
  Added error injection code for the regression test.
sql/sp.cc:
  Use the new init() member function of Parser_state and check
  its return value to handle memory allocation failures.
sql/sql_lex.cc:
  Moved memory allocation from constructor to the separate init()
  member function.
  Added error injection code for the regression test.
sql/sql_lex.h:
  Moved memory allocation from constructor to the separate init()
  member function.
sql/sql_parse.cc:
  Use the new init() member function of Parser_state and check
  its return value to handle memory allocation failures.
sql/sql_partition.cc:
  Use the new init() member function of Parser_state and check
  its return value to handle memory allocation failures.
sql/sql_prepare.cc:
  Use the new init() member function of Parser_state and check
  its return value to handle memory allocation failures.
sql/sql_trigger.cc:
  Use the new init() member function of Parser_state and check
  its return value to handle memory allocation failures.
sql/sql_view.cc:
  Use the new init() member function of Parser_state and check
  its return value to handle memory allocation failures..
sql/thr_malloc.cc:
  Added error injection code for the regression test.
parent 36be33b0
...@@ -39,5 +39,14 @@ a ...@@ -39,5 +39,14 @@ a
2 2
DROP TABLE t1; DROP TABLE t1;
# #
# Bug#42064: low memory crash when importing hex strings, in Item_hex_string::Item_hex_string
#
CREATE TABLE t1(a BLOB);
SET SESSION debug="+d,bug42064_simulate_oom";
INSERT INTO t1 VALUES("");
Got one of the listed errors
SET SESSION debug=DEFAULT;
DROP TABLE t1;
#
# End of 5.1 tests # End of 5.1 tests
# #
...@@ -46,6 +46,20 @@ SELECT * FROM t1; ...@@ -46,6 +46,20 @@ SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # Bug#42064: low memory crash when importing hex strings, in Item_hex_string::Item_hex_string
--echo #
CREATE TABLE t1(a BLOB);
SET SESSION debug="+d,bug42064_simulate_oom";
# May fail with either ER_OUT_OF_RESOURCES or EE_OUTOFMEMORY
--error ER_OUT_OF_RESOURCES, 5
INSERT INTO t1 VALUES("");
SET SESSION debug=DEFAULT;
DROP TABLE t1;
--echo # --echo #
--echo # End of 5.1 tests --echo # End of 5.1 tests
--echo # --echo #
...@@ -154,6 +154,14 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length) ...@@ -154,6 +154,14 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length)
DBUG_ASSERT(alloc_root_inited(mem_root)); DBUG_ASSERT(alloc_root_inited(mem_root));
DBUG_EXECUTE_IF("simulate_out_of_memory",
{
if (mem_root->error_handler)
(*mem_root->error_handler)();
DBUG_SET("-d,simulate_out_of_memory");
DBUG_RETURN((void*) 0); /* purecov: inspected */
});
length+=ALIGN_SIZE(sizeof(USED_MEM)); length+=ALIGN_SIZE(sizeof(USED_MEM));
if (!(next = (USED_MEM*) my_malloc(length,MYF(MY_WME)))) if (!(next = (USED_MEM*) my_malloc(length,MYF(MY_WME))))
{ {
...@@ -176,6 +184,14 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length) ...@@ -176,6 +184,14 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length)
DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root)); DBUG_PRINT("enter",("root: 0x%lx", (long) mem_root));
DBUG_ASSERT(alloc_root_inited(mem_root)); DBUG_ASSERT(alloc_root_inited(mem_root));
DBUG_EXECUTE_IF("simulate_out_of_memory",
{
/* Avoid reusing an already allocated block */
if (mem_root->error_handler)
(*mem_root->error_handler)();
DBUG_SET("-d,simulate_out_of_memory");
DBUG_RETURN((void*) 0); /* purecov: inspected */
});
length= ALIGN_SIZE(length); length= ALIGN_SIZE(length);
if ((*(prev= &mem_root->free)) != NULL) if ((*(prev= &mem_root->free)) != NULL)
{ {
......
...@@ -31,13 +31,23 @@ void *my_malloc(size_t size, myf my_flags) ...@@ -31,13 +31,23 @@ void *my_malloc(size_t size, myf my_flags)
if (!size) if (!size)
size=1; /* Safety */ size=1; /* Safety */
if ((point = (char*)malloc(size)) == NULL)
point= (char *) malloc(size);
DBUG_EXECUTE_IF("simulate_out_of_memory",
{
free(point);
point= NULL;
});
if (point == NULL)
{ {
my_errno=errno; my_errno=errno;
if (my_flags & MY_FAE) if (my_flags & MY_FAE)
error_handler_hook=fatal_error_handler_hook; error_handler_hook=fatal_error_handler_hook;
if (my_flags & (MY_FAE+MY_WME)) if (my_flags & (MY_FAE+MY_WME))
my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG+ME_NOREFRESH),size); my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_WAITTANG+ME_NOREFRESH),size);
DBUG_EXECUTE_IF("simulate_out_of_memory",
DBUG_SET("-d,simulate_out_of_memory"););
if (my_flags & MY_FAE) if (my_flags & MY_FAE)
exit(1); exit(1);
} }
......
...@@ -139,6 +139,11 @@ void *_mymalloc(size_t size, const char *filename, uint lineno, myf MyFlags) ...@@ -139,6 +139,11 @@ void *_mymalloc(size_t size, const char *filename, uint lineno, myf MyFlags)
size + /* size requested */ size + /* size requested */
4 + /* overrun mark */ 4 + /* overrun mark */
sf_malloc_endhunc); sf_malloc_endhunc);
DBUG_EXECUTE_IF("simulate_out_of_memory",
{
free(irem);
irem= NULL;
});
} }
/* Check if there isn't anymore memory avaiable */ /* Check if there isn't anymore memory avaiable */
if (!irem) if (!irem)
...@@ -159,6 +164,8 @@ void *_mymalloc(size_t size, const char *filename, uint lineno, myf MyFlags) ...@@ -159,6 +164,8 @@ void *_mymalloc(size_t size, const char *filename, uint lineno, myf MyFlags)
} }
DBUG_PRINT("error",("Out of memory, in use: %ld at line %d, '%s'", DBUG_PRINT("error",("Out of memory, in use: %ld at line %d, '%s'",
sf_malloc_max_memory,lineno, filename)); sf_malloc_max_memory,lineno, filename));
DBUG_EXECUTE_IF("simulate_out_of_memory",
DBUG_SET("-d,simulate_out_of_memory"););
if (MyFlags & MY_FAE) if (MyFlags & MY_FAE)
exit(1); exit(1);
DBUG_RETURN ((void*) 0); DBUG_RETURN ((void*) 0);
......
...@@ -1433,7 +1433,10 @@ Event_job_data::execute(THD *thd, bool drop) ...@@ -1433,7 +1433,10 @@ Event_job_data::execute(THD *thd, bool drop)
thd->set_query(sp_sql.c_ptr_safe(), sp_sql.length()); thd->set_query(sp_sql.c_ptr_safe(), sp_sql.length());
{ {
Parser_state parser_state(thd, thd->query(), thd->query_length()); Parser_state parser_state;
if (parser_state.init(thd, thd->query(), thd->query_length()))
goto end;
lex_start(thd); lex_start(thd);
if (parse_sql(thd, & parser_state, creation_ctx)) if (parse_sql(thd, & parser_state, creation_ctx))
......
...@@ -2933,6 +2933,9 @@ int my_message_sql(uint error, const char *str, myf MyFlags) ...@@ -2933,6 +2933,9 @@ int my_message_sql(uint error, const char *str, myf MyFlags)
DBUG_RETURN(0); DBUG_RETURN(0);
} }
/* When simulating OOM, skip writing to error log to avoid mtr errors */
DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_RETURN(0););
if (!thd->no_warnings_for_error && if (!thd->no_warnings_for_error &&
!(MyFlags & ME_NO_WARNING_FOR_ERROR)) !(MyFlags & ME_NO_WARNING_FOR_ERROR))
{ {
...@@ -2945,6 +2948,10 @@ int my_message_sql(uint error, const char *str, myf MyFlags) ...@@ -2945,6 +2948,10 @@ int my_message_sql(uint error, const char *str, myf MyFlags)
thd->no_warnings_for_error= FALSE; thd->no_warnings_for_error= FALSE;
} }
} }
/* When simulating OOM, skip writing to error log to avoid mtr errors */
DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_RETURN(0););
if (!thd || MyFlags & ME_NOREFRESH) if (!thd || MyFlags & ME_NOREFRESH)
sql_print_error("%s: %s",my_progname,str); /* purecov: inspected */ sql_print_error("%s: %s",my_progname,str); /* purecov: inspected */
DBUG_RETURN(0); DBUG_RETURN(0);
......
...@@ -782,7 +782,12 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, ...@@ -782,7 +782,12 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
thd->spcont= NULL; thd->spcont= NULL;
{ {
Parser_state parser_state(thd, defstr.c_ptr(), defstr.length()); Parser_state parser_state;
if (parser_state.init(thd, defstr.c_ptr(), defstr.length()))
{
ret= SP_INTERNAL_ERROR;
goto end;
}
lex_start(thd); lex_start(thd);
......
...@@ -110,39 +110,31 @@ st_parsing_options::reset() ...@@ -110,39 +110,31 @@ st_parsing_options::reset()
allows_derived= TRUE; allows_derived= TRUE;
} }
Lex_input_stream::Lex_input_stream(THD *thd,
const char* buffer, bool Lex_input_stream::init(THD *thd, const char *buff, unsigned int length)
unsigned int length)
: m_thd(thd),
yylineno(1),
yytoklen(0),
yylval(NULL),
m_ptr(buffer),
m_tok_start(NULL),
m_tok_end(NULL),
m_end_of_query(buffer + length),
m_tok_start_prev(NULL),
m_buf(buffer),
m_buf_length(length),
m_echo(TRUE),
m_cpp_tok_start(NULL),
m_cpp_tok_start_prev(NULL),
m_cpp_tok_end(NULL),
m_body_utf8(NULL),
m_cpp_utf8_processed_ptr(NULL),
next_state(MY_LEX_START),
found_semicolon(NULL),
ignore_space(test(thd->variables.sql_mode & MODE_IGNORE_SPACE)),
stmt_prepare_mode(FALSE),
in_comment(NO_COMMENT),
m_underscore_cs(NULL)
{ {
DBUG_EXECUTE_IF("bug42064_simulate_oom",
DBUG_SET("+d,simulate_out_of_memory"););
m_cpp_buf= (char*) thd->alloc(length + 1); m_cpp_buf= (char*) thd->alloc(length + 1);
DBUG_EXECUTE_IF("bug42064_simulate_oom",
DBUG_SET("-d,bug42064_simulate_oom"););
if (m_cpp_buf == NULL)
return FALSE;
m_cpp_ptr= m_cpp_buf; m_cpp_ptr= m_cpp_buf;
} m_thd= thd;
m_ptr= buff;
m_end_of_query= buff + length;
m_buf= buff;
m_buf_length= length;
ignore_space= test(thd->variables.sql_mode & MODE_IGNORE_SPACE);
return FALSE;
}
Lex_input_stream::~Lex_input_stream()
{}
/** /**
The operation is called from the parser in order to The operation is called from the parser in order to
......
...@@ -1147,9 +1147,38 @@ enum enum_comment_state ...@@ -1147,9 +1147,38 @@ enum enum_comment_state
class Lex_input_stream class Lex_input_stream
{ {
public: public:
Lex_input_stream(THD *thd, const char* buff, unsigned int length); Lex_input_stream() :
~Lex_input_stream(); yylineno(1),
yytoklen(0),
yylval(NULL),
m_tok_start(NULL),
m_tok_end(NULL),
m_tok_start_prev(NULL),
m_echo(TRUE),
m_cpp_tok_start(NULL),
m_cpp_tok_start_prev(NULL),
m_cpp_tok_end(NULL),
m_body_utf8(NULL),
m_cpp_utf8_processed_ptr(NULL),
next_state(MY_LEX_START),
found_semicolon(NULL),
stmt_prepare_mode(FALSE),
in_comment(NO_COMMENT),
m_underscore_cs(NULL)
{
}
~Lex_input_stream()
{
}
/**
Object initializer. Must be called before usage.
@retval FALSE OK
@retval TRUE Error
*/
bool init(THD *thd, const char *buff, unsigned int length);
/** /**
Set the echo mode. Set the echo mode.
...@@ -1930,10 +1959,21 @@ class Yacc_state ...@@ -1930,10 +1959,21 @@ class Yacc_state
class Parser_state class Parser_state
{ {
public: public:
Parser_state(THD *thd, const char* buff, unsigned int length) Parser_state()
: m_lip(thd, buff, length), m_yacc() : m_yacc()
{} {}
/**
Object initializer. Must be called before usage.
@retval FALSE OK
@retval TRUE Error
*/
bool init(THD *thd, const char *buff, unsigned int length)
{
return m_lip.init(thd, buff, length);
}
~Parser_state() ~Parser_state()
{} {}
......
...@@ -5944,10 +5944,15 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, ...@@ -5944,10 +5944,15 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
sp_cache_flush_obsolete(&thd->sp_proc_cache); sp_cache_flush_obsolete(&thd->sp_proc_cache);
sp_cache_flush_obsolete(&thd->sp_func_cache); sp_cache_flush_obsolete(&thd->sp_func_cache);
Parser_state parser_state(thd, inBuf, length); Parser_state parser_state;
bool err;
bool err= parse_sql(thd, & parser_state, NULL); if (!(err= parser_state.init(thd, inBuf, length)))
*found_semicolon= parser_state.m_lip.found_semicolon; {
err= parse_sql(thd, & parser_state, NULL);
*found_semicolon= parser_state.m_lip.found_semicolon;
}
else
*found_semicolon= NULL;
if (!err) if (!err)
{ {
...@@ -6033,14 +6038,17 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) ...@@ -6033,14 +6038,17 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
bool error= 0; bool error= 0;
DBUG_ENTER("mysql_test_parse_for_slave"); DBUG_ENTER("mysql_test_parse_for_slave");
Parser_state parser_state(thd, inBuf, length); Parser_state parser_state;
lex_start(thd); if (!(error= parser_state.init(thd, inBuf, length)))
mysql_reset_thd_for_next_command(thd); {
lex_start(thd);
mysql_reset_thd_for_next_command(thd);
if (!parse_sql(thd, & parser_state, NULL) && if (!parse_sql(thd, & parser_state, NULL) &&
all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
error= 1; /* Ignore question */ error= 1; /* Ignore question */
thd->end_statement(); thd->end_statement();
}
thd->cleanup_after_query(); thd->cleanup_after_query();
DBUG_RETURN(error); DBUG_RETURN(error);
} }
......
...@@ -3892,7 +3892,9 @@ bool mysql_unpack_partition(THD *thd, ...@@ -3892,7 +3892,9 @@ bool mysql_unpack_partition(THD *thd,
thd->lex= &lex; thd->lex= &lex;
thd->variables.character_set_client= system_charset_info; thd->variables.character_set_client= system_charset_info;
Parser_state parser_state(thd, part_buf, part_info_len); Parser_state parser_state;
if (parser_state.init(thd, part_buf, part_info_len))
goto end;
lex_start(thd); lex_start(thd);
*work_part_info_used= false; *work_part_info_used= false;
......
...@@ -3034,13 +3034,16 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) ...@@ -3034,13 +3034,16 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
old_stmt_arena= thd->stmt_arena; old_stmt_arena= thd->stmt_arena;
thd->stmt_arena= this; thd->stmt_arena= this;
Parser_state parser_state(thd, thd->query(), thd->query_length()); Parser_state parser_state;
parser_state.m_lip.stmt_prepare_mode= TRUE; if (!parser_state.init(thd, thd->query(), thd->query_length()))
lex_start(thd); {
parser_state.m_lip.stmt_prepare_mode= TRUE;
lex_start(thd);
error= parse_sql(thd, & parser_state, NULL) || error= parse_sql(thd, & parser_state, NULL) ||
thd->is_error() || thd->is_error() ||
init_param_array(this); init_param_array(this);
}
lex->set_trg_event_type_for_tables(); lex->set_trg_event_type_for_tables();
......
...@@ -1297,9 +1297,9 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, ...@@ -1297,9 +1297,9 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
thd->variables.sql_mode= (ulong)*trg_sql_mode; thd->variables.sql_mode= (ulong)*trg_sql_mode;
Parser_state parser_state(thd, Parser_state parser_state;
trg_create_str->str, if (parser_state.init(thd, trg_create_str->str, trg_create_str->length))
trg_create_str->length); goto err_with_lex_cleanup;
Trigger_creation_ctx *creation_ctx= Trigger_creation_ctx *creation_ctx=
Trigger_creation_ctx::create(thd, Trigger_creation_ctx::create(thd,
......
...@@ -1190,9 +1190,10 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, ...@@ -1190,9 +1190,10 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
char old_db_buf[NAME_LEN+1]; char old_db_buf[NAME_LEN+1];
LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
bool dbchanged; bool dbchanged;
Parser_state parser_state(thd, Parser_state parser_state;
table->select_stmt.str, if (parser_state.init(thd, table->select_stmt.str,
table->select_stmt.length); table->select_stmt.length))
goto err;
/* /*
Use view db name as thread default database, in order to ensure Use view db name as thread default database, in order to ensure
......
...@@ -21,8 +21,6 @@ ...@@ -21,8 +21,6 @@
extern "C" { extern "C" {
void sql_alloc_error_handler(void) void sql_alloc_error_handler(void)
{ {
sql_print_error("%s", ER(ER_OUT_OF_RESOURCES));
THD *thd= current_thd; THD *thd= current_thd;
if (thd) if (thd)
{ {
...@@ -49,6 +47,12 @@ extern "C" { ...@@ -49,6 +47,12 @@ extern "C" {
ER(ER_OUT_OF_RESOURCES)); ER(ER_OUT_OF_RESOURCES));
} }
} }
/* Skip writing to the error log to avoid mtr complaints */
DBUG_EXECUTE_IF("simulate_out_of_memory", return;);
sql_print_error("%s", ER(ER_OUT_OF_RESOURCES));
} }
} }
......
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