-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MDEV-23299: The udf_init() function cause server crash. #1643
Conversation
Hi @zbdba, Similar to other open source projects, the MariaDB Foundation needs to have shared ownership of all code that is included in the MariaDB distribution. The easiest way to achieve this is by submitting your code under the BSD-new license. The other alternative is to sign the code contribution agreement which can be found here: https://mariadb.com/kb/en/mariadb/mca/ Please indicate in a comment below, or update your PR comment, that you are contributing your new code of the whole pull request, including one or several files that are either new files or modified ones, under the BSD-new license or that you have filled out the contribution agreement and sent it. Thanks, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than testing the value on retrieval, II recommend adding CHECK constrains so that non-empty values can't be inserted.
For completeness on checks ret
can be 0, 1, 2, 4 (corresponding to STRING_RESULT=0, REAL_RESULT, INT_RESULT, DECIMAL_RESULT
(not ROW_RESULT
).
Include check
s in definition in scripts/mysql_system_tables.sql.
include removal of rows the violate checks, and alter table to add checks in scripts/mysql_system_tables_fix.sql.
include some tests of manual inserting into the func table in mysql-test/t/create_drop_udf.test
sql/sql_udf.cc
Outdated
@@ -206,6 +206,15 @@ void udf_init() | |||
DBUG_PRINT("info",("init udf record")); | |||
LEX_CSTRING name; | |||
name.str=get_field(&mem, table->field[0]); | |||
|
|||
// Check the name.str is NULL or not. | |||
if (name.str == nullptr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name in the mysql.func table is declared NOT NULL
so this isn't the case that is hit when insert into mysql.func(ret) values(1);
is performed.
name will be an empty string (or easier name.length=0
)
sql/sql_udf.cc
Outdated
if (name.str == nullptr) | ||
{ | ||
sql_print_error("Invalid row in mysql.func table for function '%.64s'", | ||
name.str); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the name.str ls nullptr it can't be used in sql_print_error. If its empty "" always then there's not much point in displaying it.
hi @an3l |
hi @grooverdan Your plan sounds better,more completely. and who can make sure this plan. I'm glad to contribute it. |
out of curiosity @zbdba, what prompted you to test the mysql.func table in this manner? |
@grooverdan , I have the same question with you, this made by our customer. |
@zbdba I'd really like a contribution from you on the revised plan that I wrote. |
@grooverdan thanks, and who can make sure this? @an3l |
Either me or @an3l will merge a completed implementation. |
@grooverdan ok, wait my pull request. |
@zbdba if possible, please don't close the current PR to open the new one, you can do all your changes in this PR, if you need help let me know. |
@an3l ok, thanks, I will do some changes in this PR. |
@grooverdan @an3l I have changed my PR, please help review my code. |
mysql-test/main/create_drop_udf.test
Outdated
@@ -35,3 +35,19 @@ SELECT metaphon('mariadb'); | |||
|
|||
DROP FUNCTION metaphon; | |||
DROP FUNCTION IF EXISTS metaphon; | |||
|
|||
--echo # | |||
--echo # MDEV-23299 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test case is missing description of MDEV, usually should be as in jira.
--echo # | ||
--echo # MDEV-23299 | ||
--echo # | ||
INSERT into mysql.func(name, ret, dl) values('example', 0, 'example.so'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are missing the test case described in jira (although is covered by other cases,but one more example would describing the formulation of the problem would be great)
INSERT into mysql.func(ret) values(0);
scripts/mysql_system_tables_fix.sql
Outdated
@@ -113,6 +113,14 @@ ALTER TABLE columns_priv | |||
|
|||
ALTER TABLE func add type enum ('function','aggregate') COLLATE utf8_general_ci NOT NULL; | |||
|
|||
# |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this complete change is not needed.
scripts/mysql_system_tables.sql
Outdated
@@ -104,7 +104,7 @@ set @had_user_table= @@warning_count != 0; | |||
|
|||
CREATE TABLE IF NOT EXISTS roles_mapping ( Host char(60) binary DEFAULT '' NOT NULL, User char(80) binary DEFAULT '' NOT NULL, Role char(80) binary DEFAULT '' NOT NULL, Admin_option enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, UNIQUE (Host, User, Role)) engine=Aria transactional=1 CHARACTER SET utf8 COLLATE utf8_bin comment='Granted roles'; | |||
|
|||
CREATE TABLE IF NOT EXISTS func ( name char(64) binary DEFAULT '' NOT NULL, ret tinyint(1) DEFAULT '0' NOT NULL, dl char(128) DEFAULT '' NOT NULL, type enum ('function','aggregate') COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (name) ) engine=Aria transactional=1 CHARACTER SET utf8 COLLATE utf8_bin comment='User defined functions'; | |||
CREATE TABLE IF NOT EXISTS func ( name char(64) COLLATE utf8_bin NOT NULL CHECK (name <> ''), ret tinyint(1) DEFAULT '0' NOT NULL CHECK (ret < 5 and ret >= 0 and ret <> 3), dl char(128) COLLATE utf8_bin NOT NULL CHECK (dl <> ''), type enum ('function','aggregate') COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (name) ) engine=Aria transactional=1 CHARACTER SET utf8 COLLATE utf8_bin comment='User defined functions'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why exactly to change collation ?
And what is reason for setting the check of the ret
? Also which logic you used for this set and why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was me that suggested the ret
constraints above. The values correspond the the value types that UDF functions can return. For completeness this should be in the commit message.
Something like
"return values (ret) can be STRING_RESULT(0), REAL_RESULT(1), INT_RESULT(2), and DECIMAL_RESULT(4), but not ROW_RESULT(3) or TIME_RESULT(5)".
The commit message should also have a title that begins with 'MDEV-23299:......'
I just checked and udf can't return TIME_RESULT(5).
What do you think about ret <>3 AND ret BETWEEN 0 AND 4)
? read-ability is the criteria.
sql/sql_udf.cc
Outdated
@@ -206,15 +206,6 @@ void udf_init() | |||
DBUG_PRINT("info",("init udf record")); | |||
LEX_CSTRING name; | |||
name.str=get_field(&mem, table->field[0]); | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we have older commit which we don't need.
You will have to do git rebase and push new changes.
mysql-test/main/create_drop_udf.test
Outdated
--echo # | ||
INSERT into mysql.func(name, ret, dl) values('example', 0, 'example.so'); | ||
|
||
--error ER_CONSTRAINT_FAILED |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and in second error
you have whitespace at the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pretty close. Thanks for the quick implemenation
mysql-test/main/create_drop_udf.test
Outdated
--error ER_CONSTRAINT_FAILED | ||
INSERT into mysql.func(name, ret, dl) values('example', 0, ''); | ||
|
||
DELETE from mysql.func; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
constrain the DELETE
to the row that was successfully inserted.
scripts/mysql_system_tables.sql
Outdated
@@ -104,7 +104,7 @@ set @had_user_table= @@warning_count != 0; | |||
|
|||
CREATE TABLE IF NOT EXISTS roles_mapping ( Host char(60) binary DEFAULT '' NOT NULL, User char(80) binary DEFAULT '' NOT NULL, Role char(80) binary DEFAULT '' NOT NULL, Admin_option enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, UNIQUE (Host, User, Role)) engine=Aria transactional=1 CHARACTER SET utf8 COLLATE utf8_bin comment='Granted roles'; | |||
|
|||
CREATE TABLE IF NOT EXISTS func ( name char(64) binary DEFAULT '' NOT NULL, ret tinyint(1) DEFAULT '0' NOT NULL, dl char(128) DEFAULT '' NOT NULL, type enum ('function','aggregate') COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (name) ) engine=Aria transactional=1 CHARACTER SET utf8 COLLATE utf8_bin comment='User defined functions'; | |||
CREATE TABLE IF NOT EXISTS func ( name char(64) COLLATE utf8_bin NOT NULL CHECK (name <> ''), ret tinyint(1) DEFAULT '0' NOT NULL CHECK (ret < 5 and ret >= 0 and ret <> 3), dl char(128) COLLATE utf8_bin NOT NULL CHECK (dl <> ''), type enum ('function','aggregate') COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (name) ) engine=Aria transactional=1 CHARACTER SET utf8 COLLATE utf8_bin comment='User defined functions'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was me that suggested the ret
constraints above. The values correspond the the value types that UDF functions can return. For completeness this should be in the commit message.
Something like
"return values (ret) can be STRING_RESULT(0), REAL_RESULT(1), INT_RESULT(2), and DECIMAL_RESULT(4), but not ROW_RESULT(3) or TIME_RESULT(5)".
The commit message should also have a title that begins with 'MDEV-23299:......'
I just checked and udf can't return TIME_RESULT(5).
What do you think about ret <>3 AND ret BETWEEN 0 AND 4)
? read-ability is the criteria.
scripts/mysql_system_tables_fix.sql
Outdated
# | ||
|
||
ALTER TABLE func MODIFY name char(64) COLLATE utf8_bin NOT NULL CHECK (name <> ''); | ||
ALTER TABLE func MODIFY ret tinyint(1) DEFAULT '0' NOT NULL CHECK (ret < 5 and ret >= 0 and ret <> 3); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ALTER TABLE can handle several modifications at with comma separated modifications. Please use this form, sql over multiple lines with indents is acceptable. Check needs to match other definition.
For alter to succeed, the contraints need to match for all elements in the table.
DELETE FROM func WHERE name='' OR ret=3 OR NOT ret BETWEEN 0 AND 4 OR dl=''
scripts/mysql_system_tables_fix.sql
Outdated
# Add CHECK for 'name','ret','dl' field. | ||
# | ||
|
||
ALTER TABLE func MODIFY name char(64) COLLATE utf8_bin NOT NULL CHECK (name <> ''); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove explicit collate.
scripts/mysql_system_tables_fix.sql
Outdated
|
||
ALTER TABLE func MODIFY name char(64) COLLATE utf8_bin NOT NULL CHECK (name <> ''); | ||
ALTER TABLE func MODIFY ret tinyint(1) DEFAULT '0' NOT NULL CHECK (ret < 5 and ret >= 0 and ret <> 3); | ||
ALTER TABLE func MODIFY dl char(128) COLLATE utf8_bin NOT NULL CHECK (dl <> ''); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no collate here either.
@an3l @grooverdan thanks for review my code, I will change the problem code, and wait for you confirm whether ret should have CHECK or not.I think maybe the ret have another result in the future? |
I'm happy either way. If UDF gets extended it will quickly hit this condition when they add a test case for it. |
@grooverdan @an3l I have change my PR again, please help review my code, thanks. |
udf_init() can crash when the name field is empty. We correct this by applying CHECK column constraints on columns in the mysql.func tables. The constrains are as follows: * name cannot be an empty string; * ret, the return type can be; * STRING_RESULT(0), * REAL_RESULT(1), * INT_RESULT(2), * and DECIMAL_RESULT(4); * but not ROW_RESULT(3) or TIME_RESULT(5). * dl, cannot be empty string Upgrades ensure that any invalid functions are deleted. closes #1643
udf_init() can crash when the name field is empty. We correct this by applying CHECK column constraints on columns in the mysql.func tables. The constrains are as follows: * name cannot be an empty string; * ret, the return type can be; * STRING_RESULT(0), * REAL_RESULT(1), * INT_RESULT(2), * and DECIMAL_RESULT(4); * but not ROW_RESULT(3) or TIME_RESULT(5). * dl, cannot be empty string Upgrades ensure that any invalid functions are deleted. closes #1643
udf_init() can crash when the name field is empty. We correct this by applying CHECK column constraints on columns in the mysql.func tables. The constrains are as follows: * name cannot be an empty string; * ret, the return type can be; * STRING_RESULT(0), * REAL_RESULT(1), * INT_RESULT(2), * and DECIMAL_RESULT(4); * but not ROW_RESULT(3) or TIME_RESULT(5). * dl, cannot be empty string Upgrades ensure that any invalid functions are deleted. closes #1643
udf_init() can crash when the name field is empty. We correct this by applying CHECK column constraints on columns in the mysql.func tables. The constrains are as follows: * name cannot be an empty string; * ret, the return type can be; * STRING_RESULT(0), * REAL_RESULT(1), * INT_RESULT(2), * and DECIMAL_RESULT(4); * but not ROW_RESULT(3) or TIME_RESULT(5). * dl, cannot be empty string Upgrades ensure that any invalid functions are deleted. closes #1643
I don't see it's needed. The root user can drop constraints and insert invalid data, it can drop the whole table, or remove essential columns. Or simply install a malicious UDF. It would be slightly safer to make the server not to crash on empty UDF names. But at the end of the day, a root user with all privileges in the server can cause any amount of damage and we cannot prevent it. |
@vuvova This situation is made by our customers. I think there is at least one way to protect against server crash,add CHECK for table or fix the problem code which make server crash. |
@grooverdan @an3l What status the PR? will merge into or just close with do nothing? |
Hi @zbdba. We haven't forgotten. Its a very busy pre-release so I hope to get a resolution after some current pressure is off the development team. I appreciate your patience and rapid delivery of working code and responding to our reviews. |
It appears a number of key development staff I wanted to catch up with an talk about this are taking leave for a few weeks. I'll catch them when they get back. Appreciate your patience. |
@grooverdan ok, thanks. |
Hi @zbdba, It looks like we dropped the ball on this one, for which I apologise. I've updated with a rebase to re-trigger the CI system and will review it for you soon. |
Hi @zbdba, Thank you again for your contribution. Unfortunately, upon reviewing this, I found that this has since been fixed in commit 4f63b6c. I am therefore going to close this PR at this time. Please don't hesitate to reopen this if you think it is in error, or contact me if you have any questions. |
No description provided.