mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
MDEV-41157 CREATE DATABASE COMMENT overflows db.opt comment buffer
Bug 1: put_dbopt() used strmov() to copy schema_comment into a fixed DATABASE_COMMENT_MAXLEN+1 buffer. validate_comment_length() only truncates comment->length in non-strict sql_mode, leaving comment->str NUL-terminated at its original (unbounded) length. strmov() copies until the source NUL, ignoring the truncated length, overflowing the destination buffer for long comments. The fix uses strmake() bounded by comment->length instead, matching the LEX_CSTRING contract (length is authoritative, str need not be NUL-terminated at length). Bug 2: write_db_opt() used strxnmov() to copy the full un-truncated comment until it ran out of buffer space mid-string with no trailing newline. Which made load_db_opt() silently discard the whole unterminated "comment=" line on the next restart, losing the comment entirely instead of just truncating it. The fix bounds the comment copy into db.opt by the already-validated comment->length via strmake(), instead of relying on the source string's own NUL terminator, matching the put_dbopt() fix. Bug 3: validate_comment_length() only runs on a COMMENT clause given in the current statement. ALTER DATABASE without one instead pulls the existing comment off disk via load_db_opt(), which never bounded it. That unvalidated length then reached write_db_opt()'s own comment= copy into its stack buffer, so a legacy or hand-edited db.opt with an overlong comment= line overflowed it on ALTER DATABASE. The fix: load_db_opt() now clamps the parsed comment to DATABASE_COMMENT_MAXLEN right when it reads the "comment=" line, so every consumer (put_dbopt(), write_db_opt()'s ALTER path) always sees an already-bounded value. The clamp itself must truncate by bytes, not characters: Well_formed_prefix()'s LEX_CSTRING overload takes a character count, but DATABASE_COMMENT_MAXLEN sizes the buffers in bytes. Bug 4: write_db_opt() still trusted validate_comment_length() to bound a directly-given COMMENT to DATABASE_COMMENT_MAXLEN bytes, but it only bounds it to DATABASE_COMMENT_MAXLEN *characters* -- so a multi-byte comment could still overflow the same fixed buffers. The fix: write_db_opt() clamps schema_comment to DATABASE_COMMENT_MAXLEN bytes itself, right after validate_comment_length() returns.
This commit is contained in:
@@ -89,3 +89,62 @@ db1 CREATE DATABASE `db1` /*!40100 DEFAULT CHARACTER SET latin1 COLLATE latin1_s
|
||||
Warnings:
|
||||
Note 1105 Database 'db1' does not have a db.opt file. You can create one with ALTER DATABASE if needed
|
||||
DROP DATABASE db1;
|
||||
#
|
||||
# MDEV-41157 CREATE DATABASE COMMENT overflows db.opt comment buffer
|
||||
#
|
||||
set session sql_mode='';
|
||||
create database db comment '<A x4000>';
|
||||
Warnings:
|
||||
Warning 4160 Comment for database 'db' is too long (max = 1024)
|
||||
# Bug 1: put_dbopt() must not overflow when storing a truncated comment
|
||||
show create database db;
|
||||
Database Create Database
|
||||
db CREATE DATABASE `db` /*!40100 DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci */ COMMENT '<A x1024>'
|
||||
flush tables;
|
||||
# Bug 2: write_db_opt() must persist a properly terminated truncated comment
|
||||
show create database db;
|
||||
Database Create Database
|
||||
db CREATE DATABASE `db` /*!40100 DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci */ COMMENT '<A x1024>'
|
||||
# Bug 3: load_db_opt() must clamp an overlong comment loaded from a legacy db.opt file
|
||||
flush tables;
|
||||
show create database db;
|
||||
Database Create Database
|
||||
db CREATE DATABASE `db` /*!40100 DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci */ COMMENT '<A x1024>'
|
||||
Warnings:
|
||||
Warning 4160 Comment in database options file './db/db.opt' is too long (max = 1024), truncated
|
||||
# Bug 3: ALTER DATABASE without COMMENT must clamp a legacy overlong comment too
|
||||
flush tables;
|
||||
alter database db character set 'latin1';
|
||||
Warnings:
|
||||
Warning 4160 Comment in database options file './db/db.opt' is too long (max = 1024), truncated
|
||||
show create database db;
|
||||
Database Create Database
|
||||
db CREATE DATABASE `db` /*!40100 DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci */ COMMENT '<A x1024>'
|
||||
drop database db;
|
||||
# Bug 4: multi-byte comment must be truncated at a character boundary
|
||||
set names utf8mb3;
|
||||
Warnings:
|
||||
Warning 4160 Comment for database 'dbmb' is too long (max = 1024)
|
||||
select char_length(schema_comment), octet_length(schema_comment)
|
||||
from information_schema.schemata where schema_name='dbmb';
|
||||
char_length(schema_comment) octet_length(schema_comment)
|
||||
341 1023
|
||||
flush tables;
|
||||
select char_length(schema_comment), octet_length(schema_comment)
|
||||
from information_schema.schemata where schema_name='dbmb';
|
||||
char_length(schema_comment) octet_length(schema_comment)
|
||||
341 1023
|
||||
drop database dbmb;
|
||||
# Bug 4: strict sql_mode must reject an overlong multi-byte comment
|
||||
set session sql_mode='TRADITIONAL';
|
||||
ERROR HY000: Comment for database 'dbmb' is too long (max = 1024)
|
||||
set session sql_mode='';
|
||||
# a 1024-character multi-byte table comment must not be byte-clamped
|
||||
select char_length(table_comment), octet_length(table_comment)
|
||||
from information_schema.tables where table_name='t1';
|
||||
char_length(table_comment) octet_length(table_comment)
|
||||
1024 3072
|
||||
drop table test.t1;
|
||||
set names default;
|
||||
set session sql_mode=default;
|
||||
# End of 10.11 tests
|
||||
|
||||
@@ -71,3 +71,77 @@ CREATE DATABASE db1;
|
||||
SHOW CREATE DATABASE db1;
|
||||
SHOW CREATE DATABASE db1;
|
||||
DROP DATABASE db1;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-41157 CREATE DATABASE COMMENT overflows db.opt comment buffer
|
||||
--echo #
|
||||
set session sql_mode='';
|
||||
--let $long_comment= `select repeat('A', 4000)`
|
||||
--replace_regex /A{4000}/<A x4000>/
|
||||
eval create database db comment '$long_comment';
|
||||
|
||||
--echo # Bug 1: put_dbopt() must not overflow when storing a truncated comment
|
||||
--replace_regex /A{1024}/<A x1024>/
|
||||
show create database db;
|
||||
flush tables;
|
||||
--echo # Bug 2: write_db_opt() must persist a properly terminated truncated comment
|
||||
--replace_regex /A{1024}/<A x1024>/
|
||||
show create database db;
|
||||
|
||||
--echo # Bug 3: load_db_opt() must clamp an overlong comment loaded from a legacy db.opt file
|
||||
--let $def_charset= `select @@character_set_server`
|
||||
--let $def_collation= `select @@collation_server`
|
||||
--remove_file $MARIADB_DATADIR/db/db.opt
|
||||
--write_file $MARIADB_DATADIR/db/db.opt EOF eval
|
||||
default-character-set=$def_charset
|
||||
default-collation=$def_collation
|
||||
comment=$long_comment
|
||||
EOF
|
||||
flush tables;
|
||||
--replace_regex /A{1024}/<A x1024>/ /[\\]/\//
|
||||
show create database db;
|
||||
|
||||
--echo # Bug 3: ALTER DATABASE without COMMENT must clamp a legacy overlong comment too
|
||||
flush tables;
|
||||
--replace_regex /[\\]/\//
|
||||
alter database db character set 'latin1';
|
||||
--replace_regex /A{1024}/<A x1024>/
|
||||
show create database db;
|
||||
|
||||
drop database db;
|
||||
|
||||
--echo # Bug 4: multi-byte comment must be truncated at a character boundary
|
||||
set names utf8mb3;
|
||||
--let $mb_comment= `select repeat(convert(0xE4B8AD using utf8mb3), 4000)`
|
||||
--disable_query_log
|
||||
eval create database dbmb comment '$mb_comment';
|
||||
--enable_query_log
|
||||
select char_length(schema_comment), octet_length(schema_comment)
|
||||
from information_schema.schemata where schema_name='dbmb';
|
||||
flush tables;
|
||||
select char_length(schema_comment), octet_length(schema_comment)
|
||||
from information_schema.schemata where schema_name='dbmb';
|
||||
drop database dbmb;
|
||||
|
||||
--echo # Bug 4: strict sql_mode must reject an overlong multi-byte comment
|
||||
set session sql_mode='TRADITIONAL';
|
||||
--disable_query_log
|
||||
--error ER_TOO_LONG_DATABASE_COMMENT
|
||||
eval create database dbmb comment '$mb_comment';
|
||||
--enable_query_log
|
||||
set session sql_mode='';
|
||||
|
||||
--echo # a 1024-character multi-byte table comment must not be byte-clamped
|
||||
--let $mb_table_comment= `select repeat(convert(0xE4B8AD using utf8mb3), 1024)`
|
||||
--disable_query_log
|
||||
eval create table test.t1 (a int) comment '$mb_table_comment';
|
||||
--enable_query_log
|
||||
select char_length(table_comment), octet_length(table_comment)
|
||||
from information_schema.tables where table_name='t1';
|
||||
drop table test.t1;
|
||||
|
||||
set names default;
|
||||
|
||||
set session sql_mode=default;
|
||||
|
||||
--echo # End of 10.11 tests
|
||||
|
||||
@@ -428,7 +428,9 @@ static my_bool put_dbopt(const char *dbname, Schema_specification_st *create)
|
||||
|
||||
if (create->schema_comment)
|
||||
{
|
||||
strmov(opt->comment.str, create->schema_comment->str);
|
||||
DBUG_ASSERT(create->schema_comment->length <= DATABASE_COMMENT_MAXLEN);
|
||||
strmake(opt->comment.str, create->schema_comment->str,
|
||||
create->schema_comment->length);
|
||||
opt->comment.length= create->schema_comment->length;
|
||||
}
|
||||
|
||||
@@ -479,6 +481,12 @@ static bool write_db_opt(THD *thd, const char *path,
|
||||
ER_TOO_LONG_DATABASE_COMMENT,
|
||||
thd->lex->name.str))
|
||||
return error;
|
||||
|
||||
const size_t byte_len= MY_MIN(create->schema_comment->length,
|
||||
(size_t) DATABASE_COMMENT_MAXLEN);
|
||||
create->schema_comment->length=
|
||||
Well_formed_prefix(system_charset_info, create->schema_comment->str,
|
||||
byte_len).length();
|
||||
}
|
||||
|
||||
if (thd->lex->sql_command == SQLCOM_ALTER_DB &&
|
||||
@@ -514,9 +522,14 @@ static bool write_db_opt(THD *thd, const char *path,
|
||||
"\n", NullS) - buf);
|
||||
|
||||
if (create->schema_comment)
|
||||
length= (ulong) (strxnmov(buf+length, sizeof(buf)-1-length,
|
||||
"comment=", create->schema_comment->str,
|
||||
"\n", NullS) - buf);
|
||||
{
|
||||
DBUG_ASSERT(create->schema_comment->length <= DATABASE_COMMENT_MAXLEN);
|
||||
char *pos= strmov(buf+length, "comment=");
|
||||
pos= strmake(pos, create->schema_comment->str,
|
||||
create->schema_comment->length);
|
||||
*pos++= '\n';
|
||||
length= (ulong) (pos - buf);
|
||||
}
|
||||
|
||||
/* Error is written by mysql_file_write */
|
||||
if (!mysql_file_write(file, (uchar*) buf, length, MYF(MY_NABP+MY_WME)))
|
||||
@@ -591,6 +604,7 @@ int load_db_opt(THD *thd, const char *path, Schema_specification_st *create)
|
||||
while (pos > buf && !my_isgraph(&my_charset_latin1, pos[-1]))
|
||||
pos--;
|
||||
*pos=0;
|
||||
char const *line_end= pos;
|
||||
if ((pos= strchr(buf, '=')))
|
||||
{
|
||||
if (!strncmp(buf,"default-character-set", (pos-buf)))
|
||||
@@ -622,7 +636,25 @@ int load_db_opt(THD *thd, const char *path, Schema_specification_st *create)
|
||||
}
|
||||
}
|
||||
else if (!strncmp(buf, "comment", (pos-buf)))
|
||||
create->schema_comment= thd->make_clex_string(pos+1, strlen(pos+1));
|
||||
{
|
||||
const LEX_CSTRING raw_comment= {pos + 1,
|
||||
(size_t) (line_end - (pos + 1))};
|
||||
const size_t byte_len= MY_MIN(raw_comment.length,
|
||||
(size_t) DATABASE_COMMENT_MAXLEN);
|
||||
const size_t comment_len=
|
||||
Well_formed_prefix(system_charset_info, raw_comment.str,
|
||||
byte_len).length();
|
||||
if (comment_len < raw_comment.length)
|
||||
{
|
||||
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
ER_TOO_LONG_DATABASE_COMMENT,
|
||||
"Comment in database options file '%-.256s' "
|
||||
"is too long (max = %u), truncated",
|
||||
path, (uint) DATABASE_COMMENT_MAXLEN);
|
||||
}
|
||||
create->schema_comment= thd->make_clex_string(raw_comment.str,
|
||||
comment_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user