Bug #53164 "SET some_session_var=ON" has the potential to crash
Submitted: 26 Apr 2010 14:31 Modified: 24 Dec 2012 8:55
Reporter: Guilhem Bichot Email Updates:
Status: Duplicate Impact on me:
None 
Category:MySQL Server: Options Severity:S3 (Non-critical)
Version:6.0-codebase-bugfixing, 5.6.99 OS:Any
Assigned to: CPU Architecture:Any

[26 Apr 2010 14:31] Guilhem Bichot
Description:
I have NOT tested with next-mr.
If a session variable accepts multiple flags, like @@optimizer_switch does
(set optimizer_switch=index_merge=off,loosescan=on,etc), and one of the possible flags starts with "on", then
  set the_variable=on;
will crash.
The crash doesn't occur in a stock version, but it will, the day we add a new flag to @@optimizer_switch and it starts with "on".
This bug was found while developing WL#4800 "optimizer trace", where a multi-flag session variable @@optimizer_trace is added and has one flag named "one_line" (so, starting with "on").

Stack trace:
#4  0x00007f91b8a5cef4 in strcpy () from /lib/libc.so.6
#5  0x0000000000ac898a in find_type (x=0xbb4c23 "ON", typelib=0x12802b8, full_name=8) at /m/bzrrepos/mysql-6.0-codebase-bugfixing2/mysys/typelib.c:120
#6  0x0000000000ac8e46 in parse_name (lib=0x12802b8, strpos=0x7f91ba0db410, end=0xbb4c25 "") at /m/bzrrepos/mysql-6.0-codebase-bugfixing2/mysys/typelib.c:279
#7  0x0000000000ac8f14 in find_set_from_flags (lib=0x12802b8, default_name=13, cur_set=3071, default_set=3071, str=0xbb4c23 "ON", length=2, err_pos=0x7f91ba0db4c8, err_len=0x7f91ba0db4ec)
    at /m/bzrrepos/mysql-6.0-codebase-bugfixing2/mysys/typelib.c:338
#8  0x0000000000735647 in Sys_var_flagset::do_check (this=0x12801e0, thd=0x181d520, var=0x1775dd8) at /m/bzrrepos/mysql-6.0-codebase-bugfixing2/sql/sys_vars.h:892
#9  0x0000000000595e97 in sys_var::check (this=0x12801e0, thd=0x181d520, var=0x1775dd8) at /m/bzrrepos/mysql-6.0-codebase-bugfixing2/sql/set_var.cc:216
#10 0x0000000000596d64 in set_var::check (this=0x1775dd8, thd=0x181d520) at /m/bzrrepos/mysql-6.0-codebase-bugfixing2/sql/set_var.cc:616
#11 0x0000000000596a63 in sql_set_variables (thd=0x181d520, var_list=0x181fc10) at /m/bzrrepos/mysql-6.0-codebase-bugfixing2/sql/set_var.cc:560
#12 0x000000000061a533 in mysql_execute_command (thd=0x181d520) at /m/bzrrepos/mysql-6.0-codebase-bugfixing2/sql/sql_parse.cc:3728

How to repeat:
take 6.0-codebase-bugfixing (haven't tested with next-mr), modify sys_vars.cc like this:
static const char *optimizer_switch_names[]=
{
  "ondex_merge", (keep the rest unchanged)
recompile and do
set optimizer_switch=on;

Suggested fix:
Note that I can work around it in the WL#4800 feature tree (using another name than "one_line" for the new flag).
The problem's analysis:
when the parser parses the =ON clause (sql_yacc.yy):
set_expr_or_default:
          expr { $$=$1; }
        | DEFAULT { $$=0; }
        | ON
          {
            $$= new (YYTHD->mem_root) Item_string("ON",  2,
                                                  system_charset_info);

this constructor calls:
  Item_string(const char *str,uint length,
              CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE,
              uint repertoire= MY_REPERTOIRE_UNICODE30)
    : m_cs_specified(FALSE)
  {
    str_value.set_or_copy_aligned(str, length, cs);

which does a "set" (not copy): it makes str_value.Ptr be == the address of the
static char array "ON".
This str_value is "res" in sys_var_flagset::do_check():
        var->save_result.ulonglong_value=
              find_set_from_flags(&typelib,
                                  typelib.count,
                                  current_value,
                                  default_value,
                                  res->ptr(), res->length(),
                                  &error, &error_len);
this function calls parse_name(), which calls find_type(), and because
"ON" is a prefix of one allowed flag ("one_line") we come to this in
find_type() (we found a partial match and we expand it in-line in the input string):
  if (!(full_name & 2))
    (void) strmov(x,typelib->type_names[findpos]);
which is incorrect as x is a static char array (segfault).
Note this guilty cast of "strpos" removing "const":
static uint parse_name(const TYPELIB *lib, const char **strpos, const char *end)
{
  const char *pos= *strpos;
  uint find= find_type((char*)pos, lib, 8);
                        ^^^^^^
If we didn't have this, the guilty strmov() would not compile...
This bug was probably introduced between
alik@sun.com-20100322131724-a0hybweujmt4ejx1 and
alfranio.correia@sun.com-20100426092226-bnkzpjs8uqkl7l16 .
The fix which I'm using in my tree is to replace 8 above by 2|8. It instructs find_type() to not change the content of "pos" (don't expand a partial match). Changing the content of "pos" looks quite wrong anyway because it may change
var=on=on,other=off
to
var=onlineother=off .
[26 Apr 2010 14:38] Guilhem Bichot
The temporary patch which I'm using in my feature tree:
=== modified file 'mysys/typelib.c'
--- mysys/typelib.c	2010-01-28 00:38:13 +0000
+++ mysys/typelib.c	2010-04-26 14:32:15 +0000
@@ -276,7 +276,11 @@
 static uint parse_name(const TYPELIB *lib, const char **strpos, const char *end)
 {
   const char *pos= *strpos;
-  uint find= find_type((char*)pos, lib, 8);
+  /*
+    tmp fix for BUG#53164: 2 instead of 8, otherwise
+    set global optimizer_trace=on; crashes
+  */
+  uint find= find_type((char*)pos, lib, 2|8);
   for (; pos != end && *pos != '=' && *pos !=',' ; pos++);
   *strpos= pos;
   return find;
[26 Apr 2010 18:04] Sveta Smirnova
Thank you for the report.

Verified as described with next-mr.
[29 Apr 2010 15:42] Guilhem Bichot
"This bug was probably introduced between
alik@sun.com-20100322131724-a0hybweujmt4ejx1 " is wrong, only the upper bound (mailto:alfranio.correia@sun.com-20100426092226-bnkzpjs8uqkl7l16) is good information.
[24 Dec 2012 8:55] Erlend Dahl
Duplicate of bug#59894: SET OPTIMIZER_SWITCH TO E OR D CAUSES INVALID MEMORY
WRITES/VALGRIND WARN