Commit 60c4b145 authored by Tatiana A. Nurnberg's avatar Tatiana A. Nurnberg

Bug#55436: buffer overflow in debug binary of dbug_buff in Field_new_decimal::store_value

There were some misunderstandings about parameters pertaining to buffer-size.

Patches fixes the reported off by one and
clarifies the documentation.

mysql-test/r/type_newdecimal.result:
  add test
mysql-test/t/type_newdecimal.test:
  add test
sql/field.cc:
  adjust buffer size by one to account for terminator.
sql/my_decimal.cc:
  adjust buffer size by one to account for terminator.
  clarify needs in comments.
sql/my_decimal.h:
  clarify buffer-size needs to prevent future off-by-one bugs.
strings/decimal.c:
  clarify buffer-size needs and parameters to prevent future off-by-one bugs
parent c99ae940
...@@ -1913,4 +1913,17 @@ group by PAY.id + 1; ...@@ -1913,4 +1913,17 @@ group by PAY.id + 1;
mult v_net_with_discount v_total mult v_net_with_discount v_total
1.0000 27.18 27.180000 1.0000 27.18 27.180000
DROP TABLE currencies, payments, sub_tasks; DROP TABLE currencies, payments, sub_tasks;
#
# Bug#55436: buffer overflow in debug binary of dbug_buff in
# Field_new_decimal::store_value
#
SET SQL_MODE='';
CREATE TABLE t1(f1 DECIMAL(44,24)) ENGINE=MYISAM;
INSERT INTO t1 SET f1 = -64878E-85;
Warnings:
Note 1265 Data truncated for column 'f1' at row 1
SELECT f1 FROM t1;
f1
0.000000000000000000000000
DROP TABLE IF EXISTS t1;
End of 5.1 tests End of 5.1 tests
...@@ -1510,5 +1510,19 @@ group by PAY.id + 1; ...@@ -1510,5 +1510,19 @@ group by PAY.id + 1;
DROP TABLE currencies, payments, sub_tasks; DROP TABLE currencies, payments, sub_tasks;
--echo #
--echo # Bug#55436: buffer overflow in debug binary of dbug_buff in
--echo # Field_new_decimal::store_value
--echo #
# this threw memory warnings on Windows. Also make sure future changes
# don't change these results, as per usual.
SET SQL_MODE='';
CREATE TABLE t1(f1 DECIMAL(44,24)) ENGINE=MYISAM;
INSERT INTO t1 SET f1 = -64878E-85;
SELECT f1 FROM t1;
DROP TABLE IF EXISTS t1;
--echo End of 5.1 tests --echo End of 5.1 tests
...@@ -2583,7 +2583,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value) ...@@ -2583,7 +2583,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value)
DBUG_ENTER("Field_new_decimal::store_value"); DBUG_ENTER("Field_new_decimal::store_value");
#ifndef DBUG_OFF #ifndef DBUG_OFF
{ {
char dbug_buff[DECIMAL_MAX_STR_LENGTH+1]; char dbug_buff[DECIMAL_MAX_STR_LENGTH+2];
DBUG_PRINT("enter", ("value: %s", dbug_decimal_as_string(dbug_buff, decimal_value))); DBUG_PRINT("enter", ("value: %s", dbug_decimal_as_string(dbug_buff, decimal_value)));
} }
#endif #endif
...@@ -2598,7 +2598,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value) ...@@ -2598,7 +2598,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value)
} }
#ifndef DBUG_OFF #ifndef DBUG_OFF
{ {
char dbug_buff[DECIMAL_MAX_STR_LENGTH+1]; char dbug_buff[DECIMAL_MAX_STR_LENGTH+2];
DBUG_PRINT("info", ("saving with precision %d scale: %d value %s", DBUG_PRINT("info", ("saving with precision %d scale: %d value %s",
(int)precision, (int)dec, (int)precision, (int)dec,
dbug_decimal_as_string(dbug_buff, decimal_value))); dbug_decimal_as_string(dbug_buff, decimal_value)));
...@@ -2673,7 +2673,7 @@ int Field_new_decimal::store(const char *from, uint length, ...@@ -2673,7 +2673,7 @@ int Field_new_decimal::store(const char *from, uint length,
} }
#ifndef DBUG_OFF #ifndef DBUG_OFF
char dbug_buff[DECIMAL_MAX_STR_LENGTH+1]; char dbug_buff[DECIMAL_MAX_STR_LENGTH+2];
DBUG_PRINT("enter", ("value: %s", DBUG_PRINT("enter", ("value: %s",
dbug_decimal_as_string(dbug_buff, &decimal_value))); dbug_decimal_as_string(dbug_buff, &decimal_value)));
#endif #endif
......
...@@ -95,10 +95,11 @@ int my_decimal2string(uint mask, const my_decimal *d, ...@@ -95,10 +95,11 @@ int my_decimal2string(uint mask, const my_decimal *d,
UNSIGNED. Hence the buffer for a ZEROFILLed value is the length UNSIGNED. Hence the buffer for a ZEROFILLed value is the length
the user requested, plus one for a possible decimal point, plus the user requested, plus one for a possible decimal point, plus
one if the user only wanted decimal places, but we force a leading one if the user only wanted decimal places, but we force a leading
zero on them. Because the type is implicitly UNSIGNED, we do not zero on them, plus one for the '\0' terminator. Because the type
need to reserve a character for the sign. For all other cases, is implicitly UNSIGNED, we do not need to reserve a character for
fixed_prec will be 0, and my_decimal_string_length() will be called the sign. For all other cases, fixed_prec will be 0, and
instead to calculate the required size of the buffer. my_decimal_string_length() will be called instead to calculate the
required size of the buffer.
*/ */
int length= (fixed_prec int length= (fixed_prec
? (fixed_prec + ((fixed_prec == fixed_dec) ? 1 : 0) + 1) ? (fixed_prec + ((fixed_prec == fixed_dec) ? 1 : 0) + 1)
...@@ -275,7 +276,7 @@ print_decimal_buff(const my_decimal *dec, const uchar* ptr, int length) ...@@ -275,7 +276,7 @@ print_decimal_buff(const my_decimal *dec, const uchar* ptr, int length)
const char *dbug_decimal_as_string(char *buff, const my_decimal *val) const char *dbug_decimal_as_string(char *buff, const my_decimal *val)
{ {
int length= DECIMAL_MAX_STR_LENGTH; int length= DECIMAL_MAX_STR_LENGTH + 1; /* minimum size for buff */
if (!val) if (!val)
return "NULL"; return "NULL";
(void)decimal2string((decimal_t*) val, buff, &length, 0,0,0); (void)decimal2string((decimal_t*) val, buff, &length, 0,0,0);
......
...@@ -55,7 +55,7 @@ C_MODE_END ...@@ -55,7 +55,7 @@ C_MODE_END
/** /**
maximum length of string representation (number of maximum decimal maximum length of string representation (number of maximum decimal
digits + 1 position for sign + 1 position for decimal point) digits + 1 position for sign + 1 position for decimal point, no terminator)
*/ */
#define DECIMAL_MAX_STR_LENGTH (DECIMAL_MAX_POSSIBLE_PRECISION + 2) #define DECIMAL_MAX_STR_LENGTH (DECIMAL_MAX_POSSIBLE_PRECISION + 2)
...@@ -212,6 +212,7 @@ inline uint32 my_decimal_precision_to_length(uint precision, uint8 scale, ...@@ -212,6 +212,7 @@ inline uint32 my_decimal_precision_to_length(uint precision, uint8 scale,
inline inline
int my_decimal_string_length(const my_decimal *d) int my_decimal_string_length(const my_decimal *d)
{ {
/* length of string representation including terminating '\0' */
return decimal_string_size(d); return decimal_string_size(d);
} }
......
...@@ -320,8 +320,8 @@ int decimal_actual_fraction(decimal_t *from) ...@@ -320,8 +320,8 @@ int decimal_actual_fraction(decimal_t *from)
from - value to convert from - value to convert
to - points to buffer where string representation to - points to buffer where string representation
should be stored should be stored
*to_len - in: size of to buffer *to_len - in: size of to buffer (incl. terminating '\0')
out: length of the actually written string out: length of the actually written string (excl. '\0')
fixed_precision - 0 if representation can be variable length and fixed_precision - 0 if representation can be variable length and
fixed_decimals will not be checked in this case. fixed_decimals will not be checked in this case.
Put number as with fixed point position with this Put number as with fixed point position with this
...@@ -338,6 +338,7 @@ int decimal2string(decimal_t *from, char *to, int *to_len, ...@@ -338,6 +338,7 @@ int decimal2string(decimal_t *from, char *to, int *to_len,
int fixed_precision, int fixed_decimals, int fixed_precision, int fixed_decimals,
char filler) char filler)
{ {
/* {intg_len, frac_len} output widths; {intg, frac} places in input */
int len, intg, frac= from->frac, i, intg_len, frac_len, fill; int len, intg, frac= from->frac, i, intg_len, frac_len, fill;
/* number digits before decimal point */ /* number digits before decimal point */
int fixed_intg= (fixed_precision ? int fixed_intg= (fixed_precision ?
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment