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.
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.