6

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

 3 years ago
source link: https://www.laruence.com/2014/06/26/2955.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

本来是发在长微博的, 不过, 鉴于, 好久没更新博客了...... 就转过来, 凑个数吧, 大家凑合着看 🙂
白忙活了近2个小时,不吐不快:
一切要从今天下午5点左右说起, 调试一个扩展, 用valgrind(valgrind-3.8.1)做例行检查, 很不幸的valgrind报告invalid read:

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

因为在PHP NG(PHP New Generation)中, 使用了新的字符串结构来保存字符串, 也就是zend_string:

而排查了半天, 我确认这个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下看看具体是什么原因:

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

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

优化成了和一个数据0x3000000001比较的一条指令....
于是, 如上面所说, 因为这个str只有22个bytes, 当尝试从16偏移处尝试读取8个字节的时候, 我们其实多读了str结构体外面的3个字节...... 于是就invalid read了
问题清楚了, GCC聪明的优化, 引起的一个无害的报告(and 0xffffffffff)............ 于是, 白忙活了.... (当然, 最好还是修复掉, 我现在打算的修复就是, 最小也要分配一个24bytes).


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK