Commit 556f5684 authored by Sergei Golubchik's avatar Sergei Golubchik

init_from_binary_frm_image: verify that we don't read beyond the image buffer

parent 2481db06
......@@ -709,7 +709,7 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags)
mysql_file_close(file, MYF(MY_WME));
share->init_from_binary_frm_image(thd, NULL, buf, stats.st_size);
error_given= true;
error_given= true; // init_from_binary_frm_image has already called my_error()
my_free(buf);
if (!share->error)
......@@ -741,8 +741,6 @@ err_not_open:
28..29 (used to be key_info_length)
They're still set, for compatibility reasons, but never read.
TODO verify that we never read data from beyond frm_length!
*/
bool TABLE_SHARE::init_from_binary_frm_image(THD *thd, const char *path,
......@@ -758,6 +756,7 @@ bool TABLE_SHARE::init_from_binary_frm_image(THD *thd, const char *path,
bool use_hash;
char *keynames, *names, *comment_pos;
const uchar *forminfo;
const uchar *frm_image_end = frm_image + frm_length;
uchar *record, *null_flags, *null_pos;
const uchar *disk_buff, *strpos;
ulong pos, record_offset;
......@@ -791,16 +790,22 @@ bool TABLE_SHARE::init_from_binary_frm_image(THD *thd, const char *path,
if (path && writefrm(path, frm_image, frm_length))
goto err;
if (frm_length < 64 + 288)
goto err;
new_field_pack_flag= frm_image[27];
new_frm_ver= (frm_image[2] - FRM_VER);
field_pack_length= new_frm_ver < 2 ? 11 : 17;
/* Position of the form in the form file. */
len = uint2korr(frm_image+4);
if (!(pos= uint4korr(frm_image + 64 + len)))
if (frm_length < 64 + len || !(pos= uint4korr(frm_image + 64 + len)))
goto err;
forminfo= frm_image + pos;
if (forminfo + 288 >= frm_image_end)
goto err;
share->frm_version= frm_image[2];
/*
Check if .frm file created by MySQL 5.0. In this case we want to
......@@ -863,6 +868,10 @@ bool TABLE_SHARE::init_from_binary_frm_image(THD *thd, const char *path,
/* Read keyinformation */
disk_buff= frm_image + uint2korr(frm_image+6);
if (disk_buff + 6 >= frm_image_end)
goto err;
if (disk_buff[0] & 0x80)
{
share->keys= keys= (disk_buff[1] << 7) | (disk_buff[0] & 0x7f);
......@@ -912,6 +921,8 @@ bool TABLE_SHARE::init_from_binary_frm_image(THD *thd, const char *path,
{
if (new_frm_ver >= 3)
{
if (strpos + 8 >= frm_image_end)
goto err;
keyinfo->flags= (uint) uint2korr(strpos) ^ HA_NOSAME;
keyinfo->key_length= (uint) uint2korr(strpos+2);
keyinfo->key_parts= (uint) strpos[4];
......@@ -921,6 +932,8 @@ bool TABLE_SHARE::init_from_binary_frm_image(THD *thd, const char *path,
}
else
{
if (strpos + 4 >= frm_image_end)
goto err;
keyinfo->flags= ((uint) strpos[0]) ^ HA_NOSAME;
keyinfo->key_length= (uint) uint2korr(strpos+1);
keyinfo->key_parts= (uint) strpos[3];
......@@ -958,6 +971,8 @@ bool TABLE_SHARE::init_from_binary_frm_image(THD *thd, const char *path,
keyinfo->rec_per_key= rec_per_key;
for (j=keyinfo->key_parts ; j-- ; key_part++)
{
if (strpos + (new_frm_ver >= 1 ? 9 : 7) >= frm_image_end)
goto err;
*rec_per_key++=0;
key_part->fieldnr= (uint16) (uint2korr(strpos) & FIELD_NR_MASK);
key_part->offset= (uint) uint2korr(strpos+2)-1;
......@@ -1014,17 +1029,25 @@ bool TABLE_SHARE::init_from_binary_frm_image(THD *thd, const char *path,
share->ext_key_parts+= keyinfo->ext_key_parts;
}
keynames=(char*) key_part;
strpos+= (strmov(keynames, (char *) strpos) - keynames)+1;
strpos+= strnmov(keynames, (char *) strpos, frm_image_end - strpos) - keynames;
if (*strpos++) // key names are \0-terminated
goto err;
//reading index comments
for (keyinfo= share->key_info, i=0; i < keys; i++, keyinfo++)
{
if (keyinfo->flags & HA_USES_COMMENT)
{
if (strpos + 2 >= frm_image_end)
goto err;
keyinfo->comment.length= uint2korr(strpos);
keyinfo->comment.str= strmake_root(&share->mem_root, (char*) strpos+2,
strpos+= 2;
if (strpos + keyinfo->comment.length >= frm_image_end)
goto err;
keyinfo->comment.str= strmake_root(&share->mem_root, (char*) strpos,
keyinfo->comment.length);
strpos+= 2 + keyinfo->comment.length;
strpos+= keyinfo->comment.length;
}
DBUG_ASSERT(test(keyinfo->flags & HA_USES_COMMENT) ==
(keyinfo->comment.length > 0));
......@@ -1039,6 +1062,9 @@ bool TABLE_SHARE::init_from_binary_frm_image(THD *thd, const char *path,
((uint2korr(frm_image+14) == 0xffff ?
uint4korr(frm_image+47) : uint2korr(frm_image+14))));
if (record_offset + share->reclength >= frm_length)
goto err;
if ((n_length= uint4korr(frm_image+55)))
{
/* Read extra data segment */
......@@ -1046,6 +1072,10 @@ bool TABLE_SHARE::init_from_binary_frm_image(THD *thd, const char *path,
DBUG_PRINT("info", ("extra segment size is %u bytes", n_length));
next_chunk= frm_image + record_offset + share->reclength;
buff_end= next_chunk + n_length;
if (buff_end >= frm_image_end)
goto err;
share->connect_string.length= uint2korr(next_chunk);
if (!(share->connect_string.str= strmake_root(&share->mem_root,
(char*) next_chunk + 2,
......@@ -1882,7 +1912,6 @@ bool TABLE_SHARE::init_from_binary_frm_image(THD *thd, const char *path,
&share->next_number_key_offset,
&share->next_number_keypart)) < 0)
goto err; // Wrong field definition
else
reg_field->flags |= AUTO_INCREMENT_FLAG;
}
......
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