mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
MDEV-17551 assert or crashed table when using blobs
The bug was that when long item-strings was converted to VARCHAR, type_handler::string_type_handler() didn't take into account max VARCHAR length. The resulting Aria temporary table was created with a VARCHAR field of length 1 when it should have been 65537. This caused MariaDB to send impossible records to ma_write() and Aria reported eventually the table as crashed. Fixed by updating Type_handler::string_type_handler() to not create too long VARCHAR fields. To make things extra safe, I also added checks in when writing dynamic Aria records to ensure we find the wrong record during write instead of during read.
This commit is contained in:
@ -686,3 +686,22 @@ DROP TABLE t1,t2;
|
|||||||
#
|
#
|
||||||
# End of 10.0 tests
|
# End of 10.0 tests
|
||||||
#
|
#
|
||||||
|
#
|
||||||
|
# MDEV-17551
|
||||||
|
# Assertion `(&(&share->intern_lock)->m_mutex)->count > 0 &&
|
||||||
|
# pthread_equal(pthread_self(), (&(&share->intern_lock)->m_mutex)->
|
||||||
|
# thread)' failed in _ma_state_info_write or ER_CRASHED_ON_USAGE
|
||||||
|
# upon SELECT with UNION
|
||||||
|
#
|
||||||
|
CREATE TABLE t1 (b BLOB, vb BLOB AS (b) VIRTUAL);
|
||||||
|
INSERT INTO t1 (b) VALUES ('foobar');
|
||||||
|
SELECT 'foo' AS f1, CONVERT( 'bar' USING latin1 ) AS f2 FROM t1
|
||||||
|
UNION
|
||||||
|
SELECT b AS f1, CONVERT( vb USING latin1 ) AS f2 FROM t1;
|
||||||
|
f1 f2
|
||||||
|
foo bar
|
||||||
|
foobar foobar
|
||||||
|
DROP TABLE t1;
|
||||||
|
#
|
||||||
|
# End of 10.3 tests
|
||||||
|
#
|
||||||
|
@ -328,3 +328,22 @@ DROP TABLE t1,t2;
|
|||||||
--echo #
|
--echo #
|
||||||
--echo # End of 10.0 tests
|
--echo # End of 10.0 tests
|
||||||
--echo #
|
--echo #
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-17551
|
||||||
|
--echo # Assertion `(&(&share->intern_lock)->m_mutex)->count > 0 &&
|
||||||
|
--echo # pthread_equal(pthread_self(), (&(&share->intern_lock)->m_mutex)->
|
||||||
|
--echo # thread)' failed in _ma_state_info_write or ER_CRASHED_ON_USAGE
|
||||||
|
--echo # upon SELECT with UNION
|
||||||
|
--echo #
|
||||||
|
|
||||||
|
CREATE TABLE t1 (b BLOB, vb BLOB AS (b) VIRTUAL);
|
||||||
|
INSERT INTO t1 (b) VALUES ('foobar');
|
||||||
|
SELECT 'foo' AS f1, CONVERT( 'bar' USING latin1 ) AS f2 FROM t1
|
||||||
|
UNION
|
||||||
|
SELECT b AS f1, CONVERT( vb USING latin1 ) AS f2 FROM t1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # End of 10.3 tests
|
||||||
|
--echo #
|
||||||
|
@ -64,7 +64,7 @@
|
|||||||
CREATE TABLE t1 (c VARBINARY(65534));
|
CREATE TABLE t1 (c VARBINARY(65534));
|
||||||
CREATE TABLE t1 (c VARBINARY(65535));
|
CREATE TABLE t1 (c VARBINARY(65535));
|
||||||
Like VARCHAR(65536), they will be converted to BLOB automatically
|
Like VARCHAR(65536), they will be converted to BLOB automatically
|
||||||
in non-sctict mode.
|
in non-strict mode.
|
||||||
*/
|
*/
|
||||||
#define MAX_FIELD_VARCHARLENGTH (65535-2-1)
|
#define MAX_FIELD_VARCHARLENGTH (65535-2-1)
|
||||||
#define MAX_FIELD_BLOBLENGTH UINT_MAX32 /* cf field_blob::get_length() */
|
#define MAX_FIELD_BLOBLENGTH UINT_MAX32 /* cf field_blob::get_length() */
|
||||||
|
@ -327,6 +327,8 @@ Type_handler::string_type_handler(uint max_octet_length)
|
|||||||
return &type_handler_long_blob;
|
return &type_handler_long_blob;
|
||||||
else if (max_octet_length >= 65536)
|
else if (max_octet_length >= 65536)
|
||||||
return &type_handler_medium_blob;
|
return &type_handler_medium_blob;
|
||||||
|
else if (max_octet_length >= MAX_FIELD_VARCHARLENGTH)
|
||||||
|
return &type_handler_blob;
|
||||||
return &type_handler_varchar;
|
return &type_handler_varchar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1372,6 +1374,7 @@ Field *Type_handler_varchar::make_conversion_table_field(TABLE *table,
|
|||||||
const Field *target)
|
const Field *target)
|
||||||
const
|
const
|
||||||
{
|
{
|
||||||
|
DBUG_ASSERT(HA_VARCHAR_PACKLENGTH(metadata) <= MAX_FIELD_VARCHARLENGTH);
|
||||||
return new(table->in_use->mem_root)
|
return new(table->in_use->mem_root)
|
||||||
Field_varstring(NULL, metadata, HA_VARCHAR_PACKLENGTH(metadata),
|
Field_varstring(NULL, metadata, HA_VARCHAR_PACKLENGTH(metadata),
|
||||||
(uchar *) "", 1, Field::NONE, &empty_clex_str,
|
(uchar *) "", 1, Field::NONE, &empty_clex_str,
|
||||||
@ -2364,6 +2367,8 @@ Field *Type_handler_varchar::make_table_field(const LEX_CSTRING *name,
|
|||||||
TABLE *table) const
|
TABLE *table) const
|
||||||
|
|
||||||
{
|
{
|
||||||
|
DBUG_ASSERT(HA_VARCHAR_PACKLENGTH(attr.max_length) <=
|
||||||
|
MAX_FIELD_VARCHARLENGTH);
|
||||||
return new (table->in_use->mem_root)
|
return new (table->in_use->mem_root)
|
||||||
Field_varstring(addr.ptr, attr.max_length,
|
Field_varstring(addr.ptr, attr.max_length,
|
||||||
HA_VARCHAR_PACKLENGTH(attr.max_length),
|
HA_VARCHAR_PACKLENGTH(attr.max_length),
|
||||||
|
@ -5426,7 +5426,12 @@ int _ma_sort_write_record(MARIA_SORT_PARAM *sort_param)
|
|||||||
info->cur_row.checksum= (*share->calc_check_checksum)(info,
|
info->cur_row.checksum= (*share->calc_check_checksum)(info,
|
||||||
sort_param->
|
sort_param->
|
||||||
record);
|
record);
|
||||||
reclength= _ma_rec_pack(info,from,sort_param->record);
|
if (!(reclength= _ma_rec_pack(info,from,sort_param->record)))
|
||||||
|
{
|
||||||
|
_ma_check_print_error(param,"Got error %d when packing record",
|
||||||
|
my_errno);
|
||||||
|
DBUG_RETURN(1);
|
||||||
|
}
|
||||||
flag=0;
|
flag=0;
|
||||||
|
|
||||||
do
|
do
|
||||||
|
@ -114,8 +114,10 @@ int maria_close(register MARIA_HA *info)
|
|||||||
share->deleting ? FLUSH_IGNORE_CHANGED : FLUSH_RELEASE))
|
share->deleting ? FLUSH_IGNORE_CHANGED : FLUSH_RELEASE))
|
||||||
error= my_errno;
|
error= my_errno;
|
||||||
unmap_file(info);
|
unmap_file(info);
|
||||||
if (((share->changed && share->base.born_transactional) ||
|
if (!internal_table &&
|
||||||
maria_is_crashed(info) || (share->temporary && !share->deleting)))
|
(((share->changed && share->base.born_transactional) ||
|
||||||
|
maria_is_crashed(info) ||
|
||||||
|
(share->temporary && !share->deleting))))
|
||||||
{
|
{
|
||||||
if (save_global_changed)
|
if (save_global_changed)
|
||||||
{
|
{
|
||||||
|
@ -224,6 +224,8 @@ my_bool _ma_write_dynamic_record(MARIA_HA *info, const uchar *record)
|
|||||||
{
|
{
|
||||||
ulong reclength= _ma_rec_pack(info,info->rec_buff + MARIA_REC_BUFF_OFFSET,
|
ulong reclength= _ma_rec_pack(info,info->rec_buff + MARIA_REC_BUFF_OFFSET,
|
||||||
record);
|
record);
|
||||||
|
if (!reclength)
|
||||||
|
return 1;
|
||||||
return (write_dynamic_record(info,info->rec_buff + MARIA_REC_BUFF_OFFSET,
|
return (write_dynamic_record(info,info->rec_buff + MARIA_REC_BUFF_OFFSET,
|
||||||
reclength));
|
reclength));
|
||||||
}
|
}
|
||||||
@ -234,6 +236,8 @@ my_bool _ma_update_dynamic_record(MARIA_HA *info, MARIA_RECORD_POS pos,
|
|||||||
{
|
{
|
||||||
uint length= _ma_rec_pack(info, info->rec_buff + MARIA_REC_BUFF_OFFSET,
|
uint length= _ma_rec_pack(info, info->rec_buff + MARIA_REC_BUFF_OFFSET,
|
||||||
record);
|
record);
|
||||||
|
if (!length)
|
||||||
|
return 1;
|
||||||
return (update_dynamic_record(info, pos,
|
return (update_dynamic_record(info, pos,
|
||||||
info->rec_buff + MARIA_REC_BUFF_OFFSET,
|
info->rec_buff + MARIA_REC_BUFF_OFFSET,
|
||||||
length));
|
length));
|
||||||
@ -258,12 +262,19 @@ my_bool _ma_write_blob_record(MARIA_HA *info, const uchar *record)
|
|||||||
reclength2= _ma_rec_pack(info,
|
reclength2= _ma_rec_pack(info,
|
||||||
rec_buff+ALIGN_SIZE(MARIA_MAX_DYN_BLOCK_HEADER),
|
rec_buff+ALIGN_SIZE(MARIA_MAX_DYN_BLOCK_HEADER),
|
||||||
record);
|
record);
|
||||||
|
if (!reclength2)
|
||||||
|
{
|
||||||
|
error= 1;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
DBUG_PRINT("info",("reclength: %lu reclength2: %lu",
|
DBUG_PRINT("info",("reclength: %lu reclength2: %lu",
|
||||||
reclength, reclength2));
|
reclength, reclength2));
|
||||||
DBUG_ASSERT(reclength2 <= reclength);
|
DBUG_ASSERT(reclength2 <= reclength);
|
||||||
error= write_dynamic_record(info,
|
error= write_dynamic_record(info,
|
||||||
rec_buff+ALIGN_SIZE(MARIA_MAX_DYN_BLOCK_HEADER),
|
rec_buff+ALIGN_SIZE(MARIA_MAX_DYN_BLOCK_HEADER),
|
||||||
reclength2);
|
reclength2);
|
||||||
|
err:
|
||||||
my_safe_afree(rec_buff, reclength);
|
my_safe_afree(rec_buff, reclength);
|
||||||
return(error != 0);
|
return(error != 0);
|
||||||
}
|
}
|
||||||
@ -293,12 +304,19 @@ my_bool _ma_update_blob_record(MARIA_HA *info, MARIA_RECORD_POS pos,
|
|||||||
my_errno= HA_ERR_OUT_OF_MEM; /* purecov: inspected */
|
my_errno= HA_ERR_OUT_OF_MEM; /* purecov: inspected */
|
||||||
return(1);
|
return(1);
|
||||||
}
|
}
|
||||||
reclength2= _ma_rec_pack(info,rec_buff+ALIGN_SIZE(MARIA_MAX_DYN_BLOCK_HEADER),
|
reclength2= _ma_rec_pack(info, rec_buff+
|
||||||
record);
|
ALIGN_SIZE(MARIA_MAX_DYN_BLOCK_HEADER),
|
||||||
|
record);
|
||||||
|
if (!reclength2)
|
||||||
|
{
|
||||||
|
error= 1;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
DBUG_ASSERT(reclength2 <= reclength);
|
DBUG_ASSERT(reclength2 <= reclength);
|
||||||
error=update_dynamic_record(info,pos,
|
error=update_dynamic_record(info,pos,
|
||||||
rec_buff+ALIGN_SIZE(MARIA_MAX_DYN_BLOCK_HEADER),
|
rec_buff+ALIGN_SIZE(MARIA_MAX_DYN_BLOCK_HEADER),
|
||||||
reclength2);
|
reclength2);
|
||||||
|
err:
|
||||||
my_safe_afree(rec_buff, reclength);
|
my_safe_afree(rec_buff, reclength);
|
||||||
return(error != 0);
|
return(error != 0);
|
||||||
}
|
}
|
||||||
@ -938,7 +956,12 @@ err:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Pack a record. Return new reclength */
|
/**
|
||||||
|
Pack a record.
|
||||||
|
|
||||||
|
@return new reclength
|
||||||
|
@return 0 in case of wrong data in record
|
||||||
|
*/
|
||||||
|
|
||||||
uint _ma_rec_pack(MARIA_HA *info, register uchar *to,
|
uint _ma_rec_pack(MARIA_HA *info, register uchar *to,
|
||||||
register const uchar *from)
|
register const uchar *from)
|
||||||
@ -1042,6 +1065,11 @@ uint _ma_rec_pack(MARIA_HA *info, register uchar *to,
|
|||||||
tmp_length= uint2korr(from);
|
tmp_length= uint2korr(from);
|
||||||
store_key_length_inc(to,tmp_length);
|
store_key_length_inc(to,tmp_length);
|
||||||
}
|
}
|
||||||
|
if (tmp_length > column->length)
|
||||||
|
{
|
||||||
|
my_errno= HA_ERR_WRONG_IN_RECORD;
|
||||||
|
DBUG_RETURN(0);
|
||||||
|
}
|
||||||
memcpy(to, from+pack_length,tmp_length);
|
memcpy(to, from+pack_length,tmp_length);
|
||||||
to+= tmp_length;
|
to+= tmp_length;
|
||||||
continue;
|
continue;
|
||||||
@ -1613,7 +1641,9 @@ my_bool _ma_cmp_dynamic_record(register MARIA_HA *info,
|
|||||||
if (!(buffer=(uchar*) my_safe_alloca(buffer_length)))
|
if (!(buffer=(uchar*) my_safe_alloca(buffer_length)))
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
reclength= _ma_rec_pack(info,buffer,record);
|
if (!(reclength= _ma_rec_pack(info,buffer,record)))
|
||||||
|
goto err;
|
||||||
|
|
||||||
record= buffer;
|
record= buffer;
|
||||||
|
|
||||||
filepos= info->cur_row.lastpos;
|
filepos= info->cur_row.lastpos;
|
||||||
|
Reference in New Issue
Block a user