diff options
author | unknown <peter@mysql.com> | 2002-10-13 21:11:01 +0400 |
---|---|---|
committer | unknown <peter@mysql.com> | 2002-10-13 21:11:01 +0400 |
commit | ceec5316baa8a9fed4eabb35d7b2f05387576239 (patch) | |
tree | 2247c37ab0c5df1551d24297c4468bd1c02625c4 /sql/field.cc | |
parent | 78aa5f7730bc9ec7df5ac51cf5be9bd1842032f4 (diff) | |
download | mariadb-git-ceec5316baa8a9fed4eabb35d7b2f05387576239.tar.gz |
Fix exponent overflow handling for decimal type. Overflow lead to crash
mysql-test/r/type_decimal.result:
Result set for new tests
mysql-test/t/type_decimal.test:
Tests for exponent overflow
sql/field.cc:
Fixes for exponent overflow handling
Diffstat (limited to 'sql/field.cc')
-rw-r--r-- | sql/field.cc | 55 |
1 files changed, 40 insertions, 15 deletions
diff --git a/sql/field.cc b/sql/field.cc index 42ddcc3b9d2..9cb13e96cc1 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -367,6 +367,7 @@ void Field_decimal::store(const char *from,uint len) /* The pointer where the field value starts (i.e., "where to write") */ char *to=ptr; uint tmp_dec, tmp_uint; + ulonglong tmp_ulonglong; /* The sign of the number : will be 0 (means positive but sign not specified), '+' or '-' @@ -380,7 +381,8 @@ void Field_decimal::store(const char *from,uint len) const char *frac_digits_from, *frac_digits_end; /* The sign of the exponent : will be 0 (means no exponent), '+' or '-' */ char expo_sign_char=0; - uint exponent=0; // value of the exponent + uint exponent=0; // value of the exponent + ulonglong exponent_ulonglong=0; /* Pointers used when digits move from the left of the '.' to the right of the '.' (explained below) @@ -474,13 +476,23 @@ void Field_decimal::store(const char *from,uint len) else expo_sign_char= '+'; /* - Read digits of the exponent and compute its value - 'exponent' overflow (e.g. if 1E10000000000000000) is not a problem - (the value of the field will be overflow anyway, or 0 anyway, - it does not change anything if the exponent is 2^32 or more + Read digits of the exponent and compute its value. + We must care about 'exponent' overflow, because as + unsigned arithmetic is "modulo", big exponents + will become small (e.g. + 1e4294967296 will become 1e0, and the field + will finally contain 1 instead of its max possible value). */ - for (;from!=end && isdigit(*from); from++) - exponent=10*exponent+(*from-'0'); + for (;from!=end && isdigit(*from); from++) + { + exponent_ulonglong=10*exponent_ulonglong+(ulonglong)(*from-'0'); + if (exponent_ulonglong>(ulonglong)UINT_MAX) + { + exponent_ulonglong=(ulonglong)UINT_MAX; + break; + } + } + exponent=(uint)(exponent_ulonglong); } /* @@ -521,15 +533,22 @@ void Field_decimal::store(const char *from,uint len) int_digits_added_zeros=2 (to make 1234500). */ + /* + Below tmp_ulongulong cannot overflow, + as int_digits_added_zeros<=exponent<4G and + (ulonglong)(int_digits_end-int_digits_from)<=max_allowed_packet<=2G and + (ulonglong)(frac_digits_from-int_digits_tail_from)<=max_allowed_packet<=2G + */ + if (!expo_sign_char) - tmp_uint=tmp_dec+(uint)(int_digits_end-int_digits_from); + tmp_ulonglong=(ulonglong)tmp_dec+(ulonglong)(int_digits_end-int_digits_from); else if (expo_sign_char == '-') { tmp_uint=min(exponent,(uint)(int_digits_end-int_digits_from)); frac_digits_added_zeros=exponent-tmp_uint; int_digits_end -= tmp_uint; frac_digits_head_end=int_digits_end+tmp_uint; - tmp_uint=tmp_dec+(uint)(int_digits_end-int_digits_from); + tmp_ulonglong=(ulonglong)tmp_dec+(ulonglong)(int_digits_end-int_digits_from); } else // (expo_sign_char=='+') { @@ -556,9 +575,9 @@ void Field_decimal::store(const char *from,uint len) int_digits_added_zeros=0; } } - tmp_uint=(tmp_dec+(uint)(int_digits_end-int_digits_from) - +(uint)(frac_digits_from-int_digits_tail_from)+ - int_digits_added_zeros); + tmp_ulonglong=(ulonglong)tmp_dec+(ulonglong)(int_digits_end-int_digits_from) + +(ulonglong)(frac_digits_from-int_digits_tail_from)+ + (ulonglong)int_digits_added_zeros; } /* @@ -569,7 +588,8 @@ void Field_decimal::store(const char *from,uint len) If the sign is defined and '-', we need one position for it */ - if (field_length < tmp_uint + (int) (sign_char == '-')) + if ((ulonglong)field_length < tmp_ulonglong + (ulonglong) (sign_char == '-')) + //the rightmost sum above cannot overflow { // too big number, change to max or min number Field_decimal::overflow(sign_char == '-'); @@ -577,6 +597,11 @@ void Field_decimal::store(const char *from,uint len) } /* + If the above test was ok, then tmp_ulonglong<4G and the following cast is valid + */ + tmp_uint=(uint)tmp_ulonglong; + + /* Tmp_left_pos is the position where the leftmost digit of the int_% parts will be written */ @@ -632,7 +657,7 @@ void Field_decimal::store(const char *from,uint len) *pos--=' '; //fill with blanks } - if (tmp_dec) // This field has decimals + // if (tmp_dec) { /* Write digits of the frac_% parts ; @@ -644,8 +669,8 @@ void Field_decimal::store(const char *from,uint len) */ pos=to+(uint)(field_length-tmp_dec); // Calculate post to '.' - *pos++='.'; right_wall=to+field_length; + if (pos != right_wall) *pos++='.'; if (expo_sign_char == '-') { |