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