diff --git a/mysql-test/main/type_bit_mdev35715.result b/mysql-test/main/type_bit_mdev35715.result new file mode 100644 index 0000000000000..7fd23eacdb4f1 --- /dev/null +++ b/mysql-test/main/type_bit_mdev35715.result @@ -0,0 +1,58 @@ +# +# MDEV-35715 UBSAN float-cast-overflow in Field_bit::store and Field_enum::store +# +# Field_bit: value >= 2^63 (outside long long range) +CREATE TABLE t1 (c BIT); +INSERT INTO t1 VALUES (1e+19); +Warnings: +Warning 1264 Out of range value for column 'c' at row 1 +SELECT HEX(c) FROM t1; +HEX(c) +1 +DROP TABLE t1; +# Field_bit: very large value +CREATE TABLE t1 (c BIT); +INSERT INTO t1 VALUES (1e+100); +Warnings: +Warning 1264 Out of range value for column 'c' at row 1 +SELECT HEX(c) FROM t1; +HEX(c) +1 +DROP TABLE t1; +# Field_bit: negative out-of-range (should clamp/warn) +CREATE TABLE t1 (c BIT); +INSERT INTO t1 VALUES (-1e+30); +Warnings: +Warning 1264 Out of range value for column 'c' at row 1 +SELECT HEX(c) FROM t1; +HEX(c) +0 +DROP TABLE t1; +# Field_enum: positive out-of-range +CREATE TABLE t1 (c ENUM('a','b','c')); +INSERT INTO t1 VALUES (-1e+30); +Warnings: +Warning 1265 Data truncated for column 'c' at row 1 +SELECT c FROM t1; +c + +DROP TABLE t1; +# Field_set: out-of-range via SET column +CREATE TABLE t1 (c SET('x','y')); +INSERT INTO t1 VALUES (-1e+30); +Warnings: +Warning 1265 Data truncated for column 'c' at row 1 +SELECT c FROM t1; +c + +DROP TABLE t1; +# Additional testcase from bug report: DOUBLE UNSIGNED ZEROFILL + BIT +CREATE TABLE t2 (c DOUBLE UNSIGNED ZEROFILL DEFAULT NULL, c2 BIT(1) DEFAULT NULL); +INSERT INTO t2 VALUES (+3E+38, +3.4E+38); +Warnings: +Warning 1264 Out of range value for column 'c' at row 1 +Warning 1264 Out of range value for column 'c2' at row 1 +SELECT HEX(c2) FROM t2; +HEX(c2) +1 +DROP TABLE t2; diff --git a/mysql-test/main/type_bit_mdev35715.test b/mysql-test/main/type_bit_mdev35715.test new file mode 100644 index 0000000000000..015d1ef52dbb9 --- /dev/null +++ b/mysql-test/main/type_bit_mdev35715.test @@ -0,0 +1,39 @@ +--echo # +--echo # MDEV-35715 UBSAN float-cast-overflow in Field_bit::store and Field_enum::store +--echo # + +--echo # Field_bit: value >= 2^63 (outside long long range) +CREATE TABLE t1 (c BIT); +INSERT INTO t1 VALUES (1e+19); +SELECT HEX(c) FROM t1; +DROP TABLE t1; + +--echo # Field_bit: very large value +CREATE TABLE t1 (c BIT); +INSERT INTO t1 VALUES (1e+100); +SELECT HEX(c) FROM t1; +DROP TABLE t1; + +--echo # Field_bit: negative out-of-range (should clamp/warn) +CREATE TABLE t1 (c BIT); +INSERT INTO t1 VALUES (-1e+30); +SELECT HEX(c) FROM t1; +DROP TABLE t1; + +--echo # Field_enum: positive out-of-range +CREATE TABLE t1 (c ENUM('a','b','c')); +INSERT INTO t1 VALUES (-1e+30); +SELECT c FROM t1; +DROP TABLE t1; + +--echo # Field_set: out-of-range via SET column +CREATE TABLE t1 (c SET('x','y')); +INSERT INTO t1 VALUES (-1e+30); +SELECT c FROM t1; +DROP TABLE t1; + +--echo # Additional testcase from bug report: DOUBLE UNSIGNED ZEROFILL + BIT +CREATE TABLE t2 (c DOUBLE UNSIGNED ZEROFILL DEFAULT NULL, c2 BIT(1) DEFAULT NULL); +INSERT INTO t2 VALUES (+3E+38, +3.4E+38); +SELECT HEX(c2) FROM t2; +DROP TABLE t2; diff --git a/sql/field.cc b/sql/field.cc index 48a1f6050aaa4..264ce028bd0fe 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -9621,7 +9621,7 @@ int Field_enum::store(const char *from,size_t length,CHARSET_INFO *cs) int Field_enum::store(double nr) { - return Field_enum::store((longlong) nr, FALSE); + return Field_enum::store(Converter_double_to_longlong(nr, false).result(), FALSE); } @@ -10249,7 +10249,7 @@ int Field_bit::store(const char *from, size_t length, CHARSET_INFO *cs) int Field_bit::store(double nr) { - return Field_bit::store((longlong) nr, FALSE); + return Field_bit::store(Converter_double_to_longlong(nr, true).result(), FALSE); } diff --git a/sql/field.h b/sql/field.h index a509c194bb101..86f2283cfb89b 100644 --- a/sql/field.h +++ b/sql/field.h @@ -5059,7 +5059,7 @@ class Field_set final :public Field_enum { int store_field(Field *from) override { return from->save_in_field(this); } int store(const char *to,size_t length,CHARSET_INFO *charset) override; int store(double nr) override - { return Field_set::store((longlong) nr, FALSE); } + { return Field_set::store(Converter_double_to_longlong(nr, false).result(), FALSE); } int store(longlong nr, bool unsigned_val) override; bool zero_pack() const override { return true; }