mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Bug#55077: Assertion failed: width > 0 && to != ((void *)0),
file .\dtoa.c The assertion failure was correct because the 'width' argument of my_gcvt() has the signed integer type, whereas the unsigned value UINT_MAX32 was being passed by the caller (Field_double::val_str()) leading to a negative width in my_gcvt(). The following chain of problems was found by further analysis: 1. The display width for a floating point number is calculated in Field_double::val_str() as either field_length or the maximum possible length of string representation of a floating point number, whichever is greater. Since in the bug's test case field_length is UINT_MAX32, we get the same value as the display width. This does not make any sense because for numeric values field_length only matters for ZEROFILL columns, otherwise it does not make sense to allocate that much memory just to print a number. Field_float::val_str() has a similar problem. 2. Even if the above wasn't the case, we would still get a crash on a slightly different test case when trying to allocate UINT_MAX32 bytes with String::alloc() because the latter does not handle such large input values correctly due to alignment overflows. 3. Even when String::alloc() is fixed to return an error when an alignment overflow occurs, there is still a problem because almost no callers check its return value, and Field_double::val_str() is not an exception (same for Field_float::val_str()). 4. Even if all of the above wasn't the case, creating a Field_double object with UINT_MAX32 as its field_length does not make much sense either, since the .frm code limits it to MAX_FIELD_CHARLENGTH (255) bytes. Such a beast can only be created by create_tmp_field_from_item() from an Item with REAL_RESULT as its result_type() and UINT_MAX32 as its max_length. 5. For the bug's test case, the above condition (REAL_RESULT Item with max_length = UINT_MAX32) was a result of Item_func_if::fix_length_and_dec() "shortcutting" aggregation of argument types when one of the arguments was a constant NULL. In this case, the attributes of the aggregated type were simply copied from the other, non-NULL argument, but max_length was still calculated as per the general, non-shortcut case, by choosing the greatest of argument's max_length, which is obviously not correct. The patch addresses all of the above problems, even though fixing the assertion failure for the particular test case would require only a subset of the above problems to be solved.
This commit is contained in:
21
sql/field.cc
21
sql/field.cc
@ -4189,6 +4189,7 @@ String *Field_float::val_str(String *val_buffer,
|
||||
String *val_ptr __attribute__((unused)))
|
||||
{
|
||||
ASSERT_COLUMN_MARKED_FOR_READ;
|
||||
DBUG_ASSERT(field_length <= MAX_FIELD_CHARLENGTH);
|
||||
float nr;
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
if (table->s->db_low_byte_first)
|
||||
@ -4199,8 +4200,13 @@ String *Field_float::val_str(String *val_buffer,
|
||||
#endif
|
||||
memcpy(&nr, ptr, sizeof(nr));
|
||||
|
||||
uint to_length=max(field_length,70);
|
||||
val_buffer->alloc(to_length);
|
||||
uint to_length= 70;
|
||||
if (val_buffer->alloc(to_length))
|
||||
{
|
||||
my_error(ER_OUT_OF_RESOURCES, MYF(0));
|
||||
return val_buffer;
|
||||
}
|
||||
|
||||
char *to=(char*) val_buffer->ptr();
|
||||
size_t len;
|
||||
|
||||
@ -4209,7 +4215,7 @@ String *Field_float::val_str(String *val_buffer,
|
||||
else
|
||||
{
|
||||
/*
|
||||
We are safe here because the buffer length is >= 70, and
|
||||
We are safe here because the buffer length is 70, and
|
||||
fabs(float) < 10^39, dec < NOT_FIXED_DEC. So the resulting string
|
||||
will be not longer than 69 chars + terminating '\0'.
|
||||
*/
|
||||
@ -4506,6 +4512,7 @@ String *Field_double::val_str(String *val_buffer,
|
||||
String *val_ptr __attribute__((unused)))
|
||||
{
|
||||
ASSERT_COLUMN_MARKED_FOR_READ;
|
||||
DBUG_ASSERT(field_length <= MAX_FIELD_CHARLENGTH);
|
||||
double nr;
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
if (table->s->db_low_byte_first)
|
||||
@ -4515,9 +4522,13 @@ String *Field_double::val_str(String *val_buffer,
|
||||
else
|
||||
#endif
|
||||
doubleget(nr,ptr);
|
||||
uint to_length= DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE;
|
||||
if (val_buffer->alloc(to_length))
|
||||
{
|
||||
my_error(ER_OUT_OF_RESOURCES, MYF(0));
|
||||
return val_buffer;
|
||||
}
|
||||
|
||||
uint to_length=max(field_length, DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE);
|
||||
val_buffer->alloc(to_length);
|
||||
char *to=(char*) val_buffer->ptr();
|
||||
size_t len;
|
||||
|
||||
|
Reference in New Issue
Block a user