最新消息:

GCC优化引起的一个”问题”

gcc admin 2973浏览 0评论

白忙活了近2个小时,不吐不快:

一切要从今天下午5点左右说起, 调试一个扩展, 用valgrind(valgrind-3.8.1)做例行检查, 很不幸的valgrind报告invalid read:

%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7-2014-06-25-%E4%B8%8B%E5%8D%886.57.05

db attach上去以后, 发现报告错误的地方是:

%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7-2014-06-25-%E4%B8%8B%E5%8D%886.58.30

因为在PHP NG(PHP New Generation)中, 使用了新的字符串结构来保存字符串, 也就是zend_string:
%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7-2014-06-25-%E4%B8%8B%E5%8D%887.00.45
而排查了半天, 我确认这个op是经过正常初始化的, 那问题出在哪里呢?

突然看到op是一个长度为1的字符串”0″, 就突然想起来, 之前我们做了个很”精细”的优化, 因为对于上面的结构体, 在64位的系统上, sizeof它, 由于padding, 实际上会得到大于8 + 8 + 4 + 1(21) 的大小(8 + 8 + 8 = 24).

所以我们不会使用一般来说的做法:

  1. str = malloc(sizeof(str) + len + 1)

来为一个长度为len的字符串申请内存. 而是会使用类似:

  1. str = malloc ((int)((str*)0)->val) + len + 1)

的方式来为一个字符串申请内存, 所以对于”0″, 我们实际上申请分配的内存是22bytes.

但, 又会有什么问题呢? 于是让我们再次db attach上去, disassmble下看看具体是什么原因:
%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7-2014-06-25-%E4%B8%8B%E5%8D%887.09.18

恩, 问题就出在f3b5这行, GCC读取了0×10(%rdx)位置上的一个word大小的数据, %rdx此时是zend_string op的指针, 而0×10偏移是str->len. 原来是因为GCC优化很聪明的把

  1. if (str->len == 1 && str->val[0] == ‘0’)

优化成了和一个数据0×3000000001比较的一条指令….

于是, 如上面所说, 因为这个str只有22个bytes, 当尝试从16偏移处尝试读取8个字节的时候, 我们其实多读了str结构体外面的3个字节…… 于是就invalid read了

问题清楚了, GCC聪明的优化, 引起的一个无害的报告(and 0xffffffffff)………… 于是, 白忙活了…. (当然, 最好还是修复掉, 我现在打算的修复就是, 最小也要分配一个24bytes).

 

转载请注明:爱开源 » GCC优化引起的一个”问题”

您必须 登录 才能发表评论!