Bug #120153 Wrong default value for default_collation_for_utf8mb4
Submitted: 25 Mar 16:15
Reporter: Kaiwang CHen (OCA) Email Updates:
Status: Open Impact on me:
None 
Category:MySQL Server: Options Severity:S3 (Non-critical)
Version:9.5 OS:Any
Assigned to: CPU Architecture:Any

[25 Mar 16:15] Kaiwang CHen
Description:
Sys_var_struct expects default value to be a pointer to pointer, however,
default_collation_for_utf8mb4 is constructed with a pointer to
my_charset_utf8mb4_0900_ai_ci, which is dereferenced to be a bad pointer in the
temporary storage save_result.ptr when
"SET GLOBAL default_collation_for_utf8mb4 = default" is processed.

There was a dirty fix to crash.

Bug #27594468: PROBLEM WHEN SETTING DEFAULT_COLLATION_FOR_UTF8MB4 TO
      DEFAULT...

However, it leaves option.def_value of Sys_var_struct in a broken state: it can
be a pointer or a pointer to pointer, and there is no reliable way to tell.

For the SET GLOBAL statement, it is fine, because the check function pulls the
correct default back before update. It might have unexpected consequences
elsewhere, since saved_value_to_string() use the same saved_result.ptr as well.

I was bitten when trying to list sysvar definitions in a running server.

How to repeat:
The default value of global system variable Sys_var_struct<CHARSET_INFO, Get_name>
default_collation_for_utf8mb4 is saved in option.def_value as longlong.  It is
actually a pointer to global variable CHARSET_INFO my_charset_utf8mb4_0900_ai_ci.

static Sys_var_struct<CHARSET_INFO, Get_name> Sys_default_collation_for_utf8mb4(
    "default_collation_for_utf8mb4",
    "Controls default collation for utf8mb4 while replicating implicit "
    "utf8mb4 collations.",
    SESSION_VAR(default_collation_for_utf8mb4), NO_CMD_LINE,
    DEFAULT(&my_charset_utf8mb4_0900_ai_ci), NO_MUTEX_GUARD, IN_BINLOG,
    ON_CHECK(check_default_collation_for_utf8mb4),
    ON_UPDATE(update_deprecated));

However, for "SET GLOBAL default_collation_for_utf8mb4 = default", Sys_var_struct
treats it as a pointer to pointer when saving to the temporary storage:

  void global_save_default(THD *, set_var *var) override {
    void **default_value = reinterpret_cast<void **>(option.def_value);
    var->save_result.ptr = *default_value;
  }

(gdb) p option.def_value
$19 = 294478752
(gdb) p (CHARSET_INFO*)option.def_value
$20 = (CHARSET_INFO *) 0x118d63a0 <my_charset_utf8mb4_0900_ai_ci>
(gdb) p var->save_result.ptr
$21 = (const void *) 0xff

Bug #27594468 fixed a crash due to this bad pointer, however, it did not fix the
broken state.

Bug #27594468: PROBLEM WHEN SETTING DEFAULT_COLLATION_FOR_UTF8MB4 TO
      DEFAULT...

diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index eee5370d2de..fd67bd83510 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -6099,6 +6099,9 @@ static bool check_default_collation_for_utf8mb4(sys_var *self, THD *thd,
     return true;
   }
 
+  if (!var->value)
+    var->save_result.ptr = reinterpret_cast<void *>(self->get_default());
+

(gdb) p var->save_result.ptr
$22 = (const void *) 0xff
(gdb) p var->value
$23 = (Item *) 0x0

(gdb) p var->save_result.ptr
$26 = (const void *) 0x118d63a0 <my_charset_utf8mb4_0900_ai_ci>

Suggested fix:
Add an pointer to my_charset_utf8mb4_0900_ai_ci, and use it to construct
default_collation_for_utf8mb4. With a fix in this way, the dirty fix to
Bug #27594468 can also be reverted.
[25 Mar 16:18] Kaiwang CHen
Enclosed is a fix which also reverts previous fix to Bug #27594468.

(*) I confirm the code being submitted is offered under the terms of the OCA, and that I am authorized to contribute it.

Contribution: bug_120153.patch (application/octet-stream, text), 2.43 KiB.