1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

MDEV-28374 UBSAN signed integer overflow PROCEDURE ANALYSE

PROCEDURE ANALYSE returns a Std (Standard deviation) which involves the
sum of squares, which can exceed the longlong datatypes.

Adjust the sum_sqr and sum value to double to acocunt that its only used
in a double context and precision isn't required.

Reviewed by: Alexander Barkov
This commit is contained in:
Daniel Black
2026-05-04 14:10:35 +10:00
parent a9a93c4fc0
commit 6eda0af40f
6 changed files with 251 additions and 33 deletions

View File

@@ -257,3 +257,105 @@ DROP TABLE t;
#
# End of 10.5 tests
#
#
# MDEV-28374 UBSAN signed integer overflow PROCEDURE ANALYSE
#
SET sql_mode='';
CREATE TABLE t (a BIGINT);
INSERT INTO t VALUES ('10000000000000'),('10000000000000');
SELECT * FROM t PROCEDURE ANALYSE();
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.t.a 10000000000000 10000000000000 14 14 0 0 10000000000000.0000 0.0000 ENUM('10000000000000') NOT NULL
DROP TABLE t;
CREATE TABLE t (a BIGINT UNSIGNED);
INSERT INTO t VALUES ('18446744073709551615'),('18446744073709551615');
SELECT * FROM t PROCEDURE ANALYSE();
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.t.a 18446744073709551615 18446744073709551615 20 20 0 0 18446744073709552000.0000 0.0000 ENUM('18446744073709551615') NOT NULL
DROP TABLE t;
CREATE TABLE t (a BIGINT,KEY a USING BTREE (a));
INSERT INTO t VALUES (0),('1'),('1'),('1'),('1'),('00010101'),('99991231'),('00101000000'),('691231000000'),('700101000000'),('991231235959'),('10000101000000'),('99991231235959'),('20030100000000'),('20030000000000');
SELECT * FROM t PROCEDURE ANALYSE();
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.t.a 0 99991231235959 1 14 1 0 10162279764883.6000 24971351159406.2300 ENUM('0','1','10101','99991231','101000000','691231000000','700101000000','991231235959','10000101000000','20030000000000','20030100000000','99991231235959') NOT NULL
DROP TABLE t;
CREATE TABLE t (c BIGINT,c2 DATE,c3 BLOB,KEY(c)) ENGINE=InnoDB;
INSERT INTO t VALUES (-1.e+308,1,1);
Warnings:
Warning 1264 Out of range value for column 'c' at row 1
Warning 1265 Data truncated for column 'c2' at row 1
SELECT * FROM t PROCEDURE ANALYSE();
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.t.c -9223372036854775808 -9223372036854775808 20 20 0 0 -9223372036854776000.0000 0.0000 ENUM('-9223372036854775808') NOT NULL
test.t.c2 0000-00-00 0000-00-00 10 10 0 0 10.0000 NULL ENUM('0000-00-00') NOT NULL
test.t.c3 1 1 1 1 0 0 1.0000 NULL ENUM('1') NOT NULL
DROP TABLE t;
CREATE TABLE t (c INT) ENGINE=InnoDB UNION=(t,t2) INSERT_METHOD=LAST;
CREATE TABLE t3 LIKE t;
INSERT INTO t VALUES (1),(2),(-685113344),(0);
INSERT INTO t3 SELECT * FROM t;
INSERT INTO t SELECT * FROM t;
INSERT INTO t SELECT * FROM t;
INSERT INTO t3 SELECT * FROM t;
SELECT * FROM t3 PROCEDURE ANALYSE();
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.t3.c -685113344 2 1 10 5 0 -171278335.2500 296662780.6209 ENUM('-685113344','0','1','2') NOT NULL
DROP TABLE t, t3;
CREATE TABLE t (c INT) ENGINE=InnoDB UNION=(t,t2) INSERT_METHOD=LAST;
INSERT INTO t VALUES (1);
INSERT INTO t VALUES (0xA9BD);
INSERT INTO t (c) VALUES (ADDTIME(NOW(),1));
Warnings:
Warning 1264 Out of range value for column 'c' at row 1
CREATE TABLE c AS SELECT 1 A;
SELECT * FROM t a,c,t b PROCEDURE ANALYSE();
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.a.c 1 2147483647 1 10 0 0 715842367.0000 1012323257.4700 ENUM('1','43453','2147483647') NOT NULL
test.c.A 1 1 1 1 0 0 1.0000 0.0000 ENUM('1') NOT NULL
test.b.c 1 2147483647 1 10 0 0 715842367.0000 1012323257.4700 ENUM('1','43453','2147483647') NOT NULL
DROP TABLE t, c;
CREATE TABLE t (c BIGINT);
INSERT INTO t VALUES (1000000000000000);
SELECT * FROM t PROCEDURE ANALYSE(0,0);
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.t.c 1000000000000000 1000000000000000 16 16 0 0 1000000000000000.0000 0.0000 BIGINT(16) UNSIGNED NOT NULL
DROP TABLE t;
CREATE SEQUENCE t CACHE=1 MAXVALUE=0 INCREMENT=-1;
SELECT * FROM t PROCEDURE ANALYSE();
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.t.next_not_cached_value 0 0 1 1 1 0 0.0000 0.0000 ENUM('0') NOT NULL
test.t.minimum_value -9223372036854775807 -9223372036854775807 20 20 0 0 -9223372036854776000.0000 0.0000 ENUM('-9223372036854775807') NOT NULL
test.t.maximum_value 0 0 1 1 1 0 0.0000 0.0000 ENUM('0') NOT NULL
test.t.start_value 0 0 1 1 1 0 0.0000 0.0000 ENUM('0') NOT NULL
test.t.increment -1 -1 2 2 0 0 -1.0000 0.0000 ENUM('-1') NOT NULL
test.t.cache_size 1 1 1 1 0 0 1.0000 0.0000 ENUM('1') NOT NULL
test.t.cycle_option 0 0 1 1 1 0 0.0000 0.0000 ENUM('0') NOT NULL
test.t.cycle_count 0 0 1 1 1 0 0.0000 0.0000 ENUM('0') NOT NULL
DROP SEQUENCE t;
CREATE SEQUENCE t CACHE+1;
SELECT * FROM t PROCEDURE ANALYSE();
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.t.next_not_cached_value 1 1 1 1 0 0 1.0000 0.0000 ENUM('1') NOT NULL
test.t.minimum_value 1 1 1 1 0 0 1.0000 0.0000 ENUM('1') NOT NULL
test.t.maximum_value 9223372036854775806 9223372036854775806 19 19 0 0 9223372036854776000.0000 0.0000 ENUM('9223372036854775806') NOT NULL
test.t.start_value 1 1 1 1 0 0 1.0000 0.0000 ENUM('1') NOT NULL
test.t.increment 1 1 1 1 0 0 1.0000 0.0000 ENUM('1') NOT NULL
test.t.cache_size 1 1 1 1 0 0 1.0000 0.0000 ENUM('1') NOT NULL
test.t.cycle_option 0 0 1 1 1 0 0.0000 0.0000 ENUM('0') NOT NULL
test.t.cycle_count 0 0 1 1 1 0 0.0000 0.0000 ENUM('0') NOT NULL
DROP SEQUENCE t;
CREATE TABLE t (c INT) ENGINE=InnoDB;
INSERT INTO t VALUES (0x31313131),(0x32323232);
INSERT INTO t VALUES (0x31313131),(0x32323232);
SELECT * FROM t LIMIT 1 PROCEDURE ANALYSE();
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.t.c 825307441 842150450 9 9 0 0 833728945.5000 8421504.5000 ENUM('825307441','842150450') NOT NULL
DROP TABLE t;
CREATE TABLE t (f BIGINT) ENGINE=InnoDB;
INSERT INTO t VALUES (1),(+1),(1),(1),(+1),(1),(1),(1),(1),(+1),(1),(1),(1),(20030000000000);
SELECT * FROM t PROCEDURE ANALYSE();
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.t.f 1 20030000000000 1 14 0 0 1430714285715.2144 5158513717681.4360 ENUM('1','20030000000000') NOT NULL
DROP TABLE t;
SET sql_mode=DEFAULT;
# End of 10.11 tests

View File

@@ -2,6 +2,7 @@
# Test of procedure analyse
#
-- source include/have_innodb.inc
--source include/have_sequence.inc
--disable_warnings
drop table if exists t1,t2;
@@ -267,3 +268,85 @@ DROP TABLE t;
--echo #
--echo # End of 10.5 tests
--echo #
--echo #
--echo # MDEV-28374 UBSAN signed integer overflow PROCEDURE ANALYSE
--echo #
SET sql_mode='';
CREATE TABLE t (a BIGINT);
INSERT INTO t VALUES ('10000000000000'),('10000000000000');
SELECT * FROM t PROCEDURE ANALYSE();
DROP TABLE t;
CREATE TABLE t (a BIGINT UNSIGNED);
INSERT INTO t VALUES ('18446744073709551615'),('18446744073709551615');
SELECT * FROM t PROCEDURE ANALYSE();
DROP TABLE t;
CREATE TABLE t (a BIGINT,KEY a USING BTREE (a));
INSERT INTO t VALUES (0),('1'),('1'),('1'),('1'),('00010101'),('99991231'),('00101000000'),('691231000000'),('700101000000'),('991231235959'),('10000101000000'),('99991231235959'),('20030100000000'),('20030000000000');
SELECT * FROM t PROCEDURE ANALYSE();
DROP TABLE t;
CREATE TABLE t (c BIGINT,c2 DATE,c3 BLOB,KEY(c)) ENGINE=InnoDB;
INSERT INTO t VALUES (-1.e+308,1,1);
SELECT * FROM t PROCEDURE ANALYSE();
DROP TABLE t;
CREATE TABLE t (c INT) ENGINE=InnoDB UNION=(t,t2) INSERT_METHOD=LAST;
CREATE TABLE t3 LIKE t;
INSERT INTO t VALUES (1),(2),(-685113344),(0);
INSERT INTO t3 SELECT * FROM t;
INSERT INTO t SELECT * FROM t;
INSERT INTO t SELECT * FROM t;
INSERT INTO t3 SELECT * FROM t;
SELECT * FROM t3 PROCEDURE ANALYSE();
DROP TABLE t, t3;
CREATE TABLE t (c INT) ENGINE=InnoDB UNION=(t,t2) INSERT_METHOD=LAST;
INSERT INTO t VALUES (1);
INSERT INTO t VALUES (0xA9BD);
INSERT INTO t (c) VALUES (ADDTIME(NOW(),1));
CREATE TABLE c AS SELECT 1 A;
SELECT * FROM t a,c,t b PROCEDURE ANALYSE();
DROP TABLE t, c;
CREATE TABLE t (c BIGINT);
INSERT INTO t VALUES (1000000000000000);
SELECT * FROM t PROCEDURE ANALYSE(0,0);
DROP TABLE t;
CREATE SEQUENCE t CACHE=1 MAXVALUE=0 INCREMENT=-1;
SELECT * FROM t PROCEDURE ANALYSE();
DROP SEQUENCE t;
CREATE SEQUENCE t CACHE+1;
SELECT * FROM t PROCEDURE ANALYSE();
DROP SEQUENCE t;
CREATE TABLE t (c INT) ENGINE=InnoDB;
INSERT INTO t VALUES (0x31313131),(0x32323232);
INSERT INTO t VALUES (0x31313131),(0x32323232);
SELECT * FROM t LIMIT 1 PROCEDURE ANALYSE();
DROP TABLE t;
CREATE TABLE t (f BIGINT) ENGINE=InnoDB;
INSERT INTO t VALUES (1),(+1),(1),(1),(+1),(1),(1),(1),(1),(+1),(1),(1),(1),(20030000000000);
SELECT * FROM t PROCEDURE ANALYSE();
DROP TABLE t;
SET sql_mode=DEFAULT;
--echo # End of 10.11 tests

View File

@@ -2356,11 +2356,26 @@ pk
3
DROP TABLE t2_fed, t1, t2;
set @@optimizer_switch=@save_optimizer_switch;
DROP SERVER s;
# End of 10.5 tests
#
# MDEV-28374 UBSAN signed integer overflow PROCEDURE ANALYSE
#
CREATE TABLE t (a BIGINT);
INSERT INTO t VALUES ('-9223372036854775808');
INSERT INTO t SELECT * FROM t;
SELECT * FROM t PROCEDURE ANALYSE(2);
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.t.a -9223372036854775808 -9223372036854775808 20 20 0 0 -9223372036854776000.0000 0.0000 BIGINT(20) NOT NULL
CREATE TABLE t_fed ENGINE=FEDERATED CONNECTION='s/t';
SELECT * FROM t_fed PROCEDURE ANALYSE(2);
Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype
test.t_fed.a -9223372036854775808 -9223372036854775808 20 20 0 0 -9223372036854776000.0000 0.0000 BIGINT(20) NOT NULL
DROP TABLE t, t_fed;
DROP SERVER s;
connection master;
DROP TABLE IF EXISTS federated.t1;
DROP DATABASE IF EXISTS federated;
connection slave;
DROP TABLE IF EXISTS federated.t1;
DROP DATABASE IF EXISTS federated;
# End of 10.11 tests

View File

@@ -2086,8 +2086,23 @@ SELECT * FROM t2_fed WHERE pk IN ( SELECT a FROM t1 );
DROP TABLE t2_fed, t1, t2;
set @@optimizer_switch=@save_optimizer_switch;
DROP SERVER s;
--echo # End of 10.5 tests
--echo #
--echo # MDEV-28374 UBSAN signed integer overflow PROCEDURE ANALYSE
--echo #
CREATE TABLE t (a BIGINT);
INSERT INTO t VALUES ('-9223372036854775808');
INSERT INTO t SELECT * FROM t;
SELECT * FROM t PROCEDURE ANALYSE(2);
CREATE TABLE t_fed ENGINE=FEDERATED CONNECTION='s/t';
SELECT * FROM t_fed PROCEDURE ANALYSE(2);
DROP TABLE t, t_fed;
DROP SERVER s;
source include/federated_cleanup.inc;
--echo # End of 10.11 tests

View File

@@ -555,12 +555,12 @@ void field_decimal::add()
}
}
void field_longlong::add()
{
char buff[MAX_FIELD_WIDTH];
longlong num = item->val_int();
uint length = (uint) (longlong10_to_str(num, buff, -10) - buff);
longlong numlong = item->val_int();
double num = (double) numlong;
uint length = (uint) (longlong10_to_str(numlong, buff, -10) - buff);
TREE_ELEMENT *element;
if (item->null_value)
@@ -568,12 +568,12 @@ void field_longlong::add()
nulls++;
return;
}
if (num == 0)
if (num == 0.0)
empty++;
if (room_in_tree)
{
if (!(element = tree_insert(&tree, (void*) &num, 0, tree.custom_arg)))
if (!(element = tree_insert(&tree, (void*) &numlong, 0, tree.custom_arg)))
{
room_in_tree = 0; // Remove tree, out of RAM ?
delete_tree(&tree, 0);
@@ -592,7 +592,8 @@ void field_longlong::add()
if (!found)
{
found = 1;
min_arg = max_arg = sum = num;
min_arg = max_arg = numlong;
sum = num;
sum_sqr = num * num;
min_length = max_length = length;
}
@@ -604,10 +605,10 @@ void field_longlong::add()
min_length = length;
if (length > max_length)
max_length = length;
if (compare_longlong(&num, &min_arg) < 0)
min_arg = num;
if (compare_longlong(&num, &max_arg) > 0)
max_arg = num;
if (compare_longlong(&numlong, &min_arg) < 0)
min_arg = numlong;
if (compare_longlong(&numlong, &max_arg) > 0)
max_arg = numlong;
}
} // field_longlong::add
@@ -615,8 +616,9 @@ void field_longlong::add()
void field_ulonglong::add()
{
char buff[MAX_FIELD_WIDTH];
longlong num = item->val_int();
uint length = (uint) (longlong10_to_str(num, buff, 10) - buff);
ulonglong numlong = item->val_int();
double num= (double) numlong;
uint length = (uint) (longlong10_to_str(numlong, buff, 10) - buff);
TREE_ELEMENT *element;
if (item->null_value)
@@ -629,7 +631,7 @@ void field_ulonglong::add()
if (room_in_tree)
{
if (!(element = tree_insert(&tree, (void*) &num, 0, tree.custom_arg)))
if (!(element = tree_insert(&tree, (void*) &numlong, 0, tree.custom_arg)))
{
room_in_tree = 0; // Remove tree, out of RAM ?
delete_tree(&tree, 0);
@@ -648,7 +650,8 @@ void field_ulonglong::add()
if (!found)
{
found = 1;
min_arg = max_arg = sum = num;
min_arg = max_arg = numlong;
sum = num;
sum_sqr = num * num;
min_length = max_length = length;
}
@@ -660,10 +663,10 @@ void field_ulonglong::add()
min_length = length;
if (length > max_length)
max_length = length;
if (compare_ulonglong((ulonglong*) &num, &min_arg) < 0)
min_arg = num;
if (compare_ulonglong((ulonglong*) &num, &max_arg) > 0)
max_arg = num;
if (compare_ulonglong(&numlong, &min_arg) < 0)
min_arg = numlong;
if (compare_ulonglong(&numlong, &max_arg) > 0)
max_arg = numlong;
}
} // field_ulonglong::add

View File

@@ -234,11 +234,11 @@ int collect_longlong(void *element, element_count count, void *info);
class field_longlong: public field_info
{
longlong min_arg, max_arg;
longlong sum, sum_sqr;
double sum, sum_sqr;
public:
field_longlong(Item* a, analyse* b) :field_info(a,b),
min_arg(0), max_arg(0), sum(0), sum_sqr(0)
min_arg(0), max_arg(0), sum(0), sum_sqr(0.0)
{
init_tree(&tree, 0, 0, sizeof(longlong), compare_longlong2, NULL, NULL,
MYF(MY_THREAD_SPECIFIC));
@@ -263,9 +263,9 @@ public:
s->set_real((double) 0.0, 1,my_thd_charset);
else
{
double tmp2 = ((sum_sqr - sum * sum / (tmp - nulls)) /
(tmp - nulls));
s->set_real(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG,my_thd_charset);
double tmp2 = (sum_sqr - sum * sum / (tmp - nulls)) /
(tmp - nulls);
s->set_real(tmp2 <= 0.0 ? 0.0 : sqrt(tmp2), DEC_IN_AVG,my_thd_charset);
}
return s;
}
@@ -280,7 +280,7 @@ int collect_ulonglong(void *element, element_count count, void *info);
class field_ulonglong: public field_info
{
ulonglong min_arg, max_arg;
ulonglong sum, sum_sqr;
double sum, sum_sqr;
public:
field_ulonglong(Item* a, analyse * b) :field_info(a,b),
@@ -298,8 +298,8 @@ public:
if (!(rows - nulls))
s->set_real((double) 0.0, 1,my_thd_charset);
else
s->set_real((ulonglong2double(sum) / ulonglong2double(rows - nulls)),
DEC_IN_AVG,my_thd_charset);
s->set_real(sum / ulonglong2double(rows - nulls),
DEC_IN_AVG, my_thd_charset);
return s;
}
String *std(String *s, ha_rows rows) override
@@ -309,10 +309,10 @@ public:
s->set_real((double) 0.0, 1,my_thd_charset);
else
{
double tmp2 = ((ulonglong2double(sum_sqr) -
ulonglong2double(sum * sum) / (tmp - nulls)) /
(tmp - nulls));
s->set_real(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG,my_thd_charset);
double tmp2 = (sum_sqr -
(sum * sum) / (tmp - nulls)) /
(tmp - nulls);
s->set_real(tmp2 <= 0.0 ? 0.0 : sqrt(tmp2), DEC_IN_AVG,my_thd_charset);
}
return s;
}