From 318077c4f9d57e106eecd15c5e84ffeacbc799c5 Mon Sep 17 00:00:00 2001 From: Sujatha Sivakumar Date: Mon, 24 Jun 2013 11:11:55 +0530 Subject: [PATCH] Bug#16753869:INCORRECT TRUNCATION OF LONG SET EXPRESSION IN LOAD DATA CAN CAUSE SQL INJECTION Problem: ======= A long SET expression in LOAD DATA is incorrectly truncated when written to the binary log. Analysis: ======== LOAD DATA statements are reconstructed once again before they are written to the binary log. When SET clauses are specified as part of LOAD DATA statement, these SET clause user command strings need to be stored as it is inorder to reconstruct the original user command. At present these strings are stored as part of SET clause item tree's top most Item node's name itself which is incorrect. As an Item::name can be of MAX_ALIAS_NAME (256) size. Hence the name will get truncated to "255". Because of this the rewritten LOAD DATA statement will be terminated incorrectly. When this statment is read back by the mysqlbinlog tool it reads a starting single quote and continuos to read till it finds an ending quote. Hence any statement written post ending quote will be considered as a new statement. Fix: === As name field has length restriction the string value should not be stored in Item::name. A new String list is maintained to store the SET expression values and this list is read during reconstrution. --- sql/sql_lex.cc | 1 + sql/sql_lex.h | 7 +++++++ sql/sql_load.cc | 11 ++++++----- sql/sql_yacc.yy | 13 ++++++++++--- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 3c9e6955151..bc313ed0486 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -372,6 +372,7 @@ void lex_start(THD *thd) /* 'parent_lex' is used in init_query() so it must be before it. */ lex->select_lex.parent_lex= lex; lex->select_lex.init_query(); + lex->load_set_str_list.empty(); lex->value_list.empty(); lex->update_list.empty(); lex->set_var_list.empty(); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index e4fd6f42a48..59278b80ec4 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2286,6 +2286,13 @@ struct LEX: public Query_tables_list List col_list; List ref_list; + /* + A list of strings is maintained to store the SET clause command user strings + which are specified in load data operation. This list will be used + during the reconstruction of "load data" statement at the time of writing + to binary log. + */ + List load_set_str_list; List interval_list; List users_list; List columns; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 611c32696ea..2e85cb105b0 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -672,7 +672,8 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, *p= NULL; size_t pl= 0; List fv; - Item *item, *val; + Item *item; + String *str; String pfield, pfields; int n; const char *tbl= table_name_arg; @@ -726,18 +727,18 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, if (!thd->lex->update_list.is_empty()) { List_iterator lu(thd->lex->update_list); - List_iterator lv(thd->lex->value_list); + List_iterator ls(thd->lex->load_set_str_list); pfields.append(" SET "); n= 0; while ((item= lu++)) { - val= lv++; + str= ls++; if (n++) pfields.append(", "); append_identifier(thd, &pfields, item->name, strlen(item->name)); - pfields.append(val->name); + pfields.append((char *)str->ptr()); } } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5ab7ae4a674..13c547cb9f7 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11727,10 +11727,17 @@ load_data_set_elem: simple_ident_nospvar equal remember_name expr_or_default remember_end { LEX *lex= Lex; - if (lex->update_list.push_back($1) || - lex->value_list.push_back($4)) + uint length= (uint) ($5 - $3); + String *val= new (YYTHD->mem_root) String($3, + length, + YYTHD->charset()); + if (val == NULL) + MYSQL_YYABORT; + if (lex->update_list.push_back($1) || + lex->value_list.push_back($4) || + lex->load_set_str_list.push_back(val)) MYSQL_YYABORT; - $4->set_name($3, (uint) ($5 - $3), YYTHD->charset()); + $4->set_name($3, length, YYTHD->charset()); } ;