Bug #121332 Empty --datadir= value not handled by the Bug#39181231 fix, asserting mysqld in debug builds
Submitted: 21 Sep 7:54 Modified: 21 Sep 8:34
Reporter: Varun Nagaraju (OCA) Email Updates:
Status: Open Impact on me:
None 
Category:MySQL Server: Options Severity:S3 (Non-critical)
Version:8.4.11 OS:Any
Assigned to: CPU Architecture:Any

[21 Sep 7:54] Varun Nagaraju
Description:
On a debug build, starting the server with an empty --datadir value aborts on
an assertion during early startup, before anything is written to the error log:

  mysqld: sql/mysqld.cc:2199: bool initialize_manifest_file_components():
          Assertion `strlen(mysql_real_data_home) > 0' failed.

This is a regression from the fix for Bug#39181231 (commit e2863e9).
Plugin_and_data_dir_option_parser::Plugin_and_data_dir_option_parser() now does
memset(mysql_real_data_home, 0, sizeof(...)) followed by strncpy() of the option
value. Previously it used memcpy(dst, src, strlen(src)), which for an empty
value copied zero bytes and left the compiled-in default ("data/", installed by
strmake(mysql_real_data_home, get_relative_path(MYSQL_DATADIR), ...) earlier in
mysqld_main()) in place. The new unconditional memset clears that default, and
strncpy of "" leaves the buffer empty.

The existing guard at sql/mysqld.cc:8836

  if (!datadir) { ... /* keep the current value */ ... }

covers a missing option only. --datadir is GET_STR/OPT_ARG, so --datadir= yields
a non-NULL pointer to an empty string and the guard is bypassed.

--plugin-dir= has the same gap in the same constructor; it is currently harmless
only because plugindir_ falls back to get_relative_path(PLUGINDIR) later.

How to repeat:
Debug build:

machine:~/mysql/84/bld$ ./runtime_output_directory/mysqld --no-defaults --datadir= --secure-file-priv="" -p
Enter password: 
mysqld: /home/mysql/84/mysql-server/sql/mysqld.cc:2199: bool initialize_manifest_file_components(): Assertion `strlen(mysql_real_data_home) > 0' failed.
Aborted (core dumped)
machine:~/mysql/84/bld$ echo $?
134

This issue was revelaed by main.keyring_migration_password MTR test failure.
keyring_migration_password spawns mysqld with --datadir=$MYSQLD_DATADIR,
but $MYSQLD_DATADIR is not set by mysql-test-run.pl and the test never defines
it, so the option expands to a literal --datadir= ... i.e. --datadir= . The server
therefore dies on the assertion before it can log "Aborting", and the test fails
with:
  Pattern "\[ERROR\] \[MY-[0-9]+] \[Server\] Aborting" not found

Suggested fix:
Treat an empty value the same as a missing one in
  Plugin_and_data_dir_option_parser, for both options:

    -  if (!datadir) {
    +  if (datadir == nullptr || datadir[0] == '\0') {

    -                  plugindir ? plugindir : get_relative_path(PLUGINDIR),
    +                  (plugindir != nullptr && plugindir[0] != '\0')
    +                      ? plugindir
    +                      : get_relative_path(PLUGINDIR),

trunk already has the equivalent guard in the rewritten
Manifest_file_option_parser_helper, so this is needed on 8.4 only.

Separately, main.keyring_migration_password should pass a private datadir under
$MYSQL_TMP_DIR instead of the undefined $MYSQLD_DATADIR — as written, defining
that variable "correctly" would point the spawned instance at the running
server's datadir.
[21 Sep 8:34] Varun Nagaraju
A contribution has been submitted on the github repo https://github.com/mysql/mysql-server/pull/774