Skip to content

Commit 5537235

Browse files
committed
Bug#21823135 INVALID READ OF MEMORY FREED BY GIS_WKB_RAW_FREE
Issue: The cmp_item_string::store_value() doesn't copy the string even if value.is_alloced() is false, which means the string buffer referenced by 'value' doesn't belong to 'value' but the cmp_item_string simply use this buffer anyway, assuming the buffer will be always valid whenever it's accessed. This is wrong in itself. The way some GIS functions work is to return geometry blob buffer allocated by Boost.Geometry without duplicating it, and free this buffer next time the same function is called. Such behavior breaks above wrong assumption and hence the memory issue. Fix: In cmp_item_string::store_value(), if 'value' 's referenced buffer was not allocated by itself, duplicate its string.
1 parent 24902c1 commit 5537235

File tree

4 files changed

+41
-3
lines changed

4 files changed

+41
-3
lines changed

mysql-test/r/gis.result

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2947,3 +2947,13 @@ ST_ISVALID(ST_INTERSECTION(ST_GEOMFROMTEXT('POLYGON((5 6,-15
29472947
-13,1 -8,5 6))'), ST_GEOMFROMTEXT('POLYGON((0 8,-19 6,18 -17,20 8,11 17,0
29482948
8),(3 2,3 -1,1 0,3 2),(1 3,4 4,0 -1,1 3))')))
29492949
1
2950+
#
2951+
# Bug#21823135 INVALID READ OF MEMORY FREED BY GIS_WKB_RAW_FREE
2952+
#
2953+
SELECT point(1,1) IN ('1',1,'1') AS res;
2954+
res
2955+
0
2956+
SELECT st_centroid(point(1,1)) IN ('1',1,'1') AS res;
2957+
res
2958+
0
2959+
DO st_centroid(point(1,1)) IN ('1',1,'1');

mysql-test/t/gis.test

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2771,3 +2771,11 @@ SELECT ST_ISVALID(ST_INTERSECTION(ST_GEOMFROMTEXT('POLYGON((0 5,-6
27712771
SELECT ST_ISVALID(ST_INTERSECTION(ST_GEOMFROMTEXT('POLYGON((5 6,-15
27722772
-13,1 -8,5 6))'), ST_GEOMFROMTEXT('POLYGON((0 8,-19 6,18 -17,20 8,11 17,0
27732773
8),(3 2,3 -1,1 0,3 2),(1 3,4 4,0 -1,1 3))')));
2774+
2775+
2776+
--echo #
2777+
--echo # Bug#21823135 INVALID READ OF MEMORY FREED BY GIS_WKB_RAW_FREE
2778+
--echo #
2779+
SELECT point(1,1) IN ('1',1,'1') AS res;
2780+
SELECT st_centroid(point(1,1)) IN ('1',1,'1') AS res;
2781+
DO st_centroid(point(1,1)) IN ('1',1,'1');

sql-common/sql_string.cc

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,17 +181,37 @@ bool String::copy()
181181
before copying and the old buffer freed. Character set information is also
182182
copied.
183183
184+
If str is the same as this and str doesn't own its buffer, a
185+
new buffer is allocated and it's owned by str.
186+
184187
@param str The string whose internal buffer is to be copied.
185188
186189
@retval false Success.
187190
@retval true Memory allocation failed.
188191
*/
189192
bool String::copy(const String &str)
190193
{
194+
/*
195+
If &str == this and it owns the buffer, this operation is a no-op, so skip
196+
the meaningless copy. Otherwise if we do, we will read freed memory at
197+
the memmove call below.
198+
*/
199+
if (&str == this && str.is_alloced())
200+
return false;
201+
202+
/*
203+
If a String s doesn't own its buffer, here we should allocate
204+
a new buffer owned by s and copy the contents there. But alloc()
205+
will change this->m_ptr and this->m_length, and if this == &str, this
206+
will also change str->m_ptr and str->m_length, so we need to save
207+
these values first.
208+
*/
209+
const size_t str_length= str.m_length;
210+
const char *str_ptr= str.m_ptr;
191211
if (alloc(str.m_length))
192212
return true;
193-
m_length= str.m_length;
194-
memmove(m_ptr, str.m_ptr, m_length); // May be overlapping
213+
m_length= str_length;
214+
memmove(m_ptr, str_ptr, m_length); // May be overlapping
195215
m_ptr[m_length]= 0;
196216
m_charset= str.m_charset;
197217
return false;

sql/item_cmpfunc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1429,7 +1429,7 @@ class cmp_item_string : public cmp_item_scalar
14291429
virtual void store_value(Item *item)
14301430
{
14311431
String *res= item->val_str(&value);
1432-
if(res && (res != &value))
1432+
if (res && (res != &value || !res->is_alloced()))
14331433
{
14341434
// 'res' may point in item's transient internal data, so make a copy
14351435
value.copy(*res);

0 commit comments

Comments
 (0)