对 mysql 执行如下语句:

会导致mysql crash(signal 11)。

崩溃堆栈如下:

Bug复现小贴士 一条select语句搞挂MySQL Server? 当然还是需要苛刻条件的:

  • 接着该列不能有索引, 确保逻辑走到filesort(在对索引列做GROUP BY/ORDER BY时直接走索引);
  • 之后要配备足够小的, 和足够量大的数据撑满 sort_buffer,如@@sort_buffer_size = 32768时,40行数据就可以触发;

BOOM!

搞完破坏, 我们来看问题怎么解。

现在我们沿着执行路线来探索 mysql 是如何一步步挂掉的,在 select 语句中使用 order by/group by 语句时,server 通常调用排序,主要通过索引或者 filesort 来实现排序,在 group by/order by 的列上不存在索引时,server 会选择使用 filesort,其主要逻辑见 filesort.cc:filesort()。这里还会涉及到一个变量,sort_buffer_size,当需要排序的数据量超过 大小时,server 会将数据划分为 trunks,这时调用 merge_many_buffers()。随后一路调用到 mysys/ptr_cmp.c 文件中的比较函数,这里的比较函数是按字节进行的,每四个字节为一个比较单位,当传入的参数长度小于4时,会调用 ,而在上节的调用栈可以看到,最后 crash 就是在这个函数里。函数槽点如下:

在 lengh == 0 时,while 里就会根本停不下来,直到被比较的两位指针不停自加到一个不能访问的内存区域,逼迫系统用 signal 11 杀死 mysql server。

比较长度为0的字符串本身是个意外, 所以解决方案就是添加一个辅助函数 ptr_compare_length_zero,在 length 为0时直接返回0,在做排序函数分派时,将长度为0的比较指派到。 因此,想搞挂MySQL Server,这条路已经被堵上了,还是多修bug少搞破坏比较好 :-)

  1. 官方fix1
  2. 官方fix2 b62c5daa646434290c9b2d1c9b162487cb8edf04