std::regex_replace 引起的core问题追踪

发现问题

线上预警出现了 core dump 导致应用重启,分析后,报错如下:

http://blog.bruceding.com/wp-content/uploads/2018/03/WechatIMG3955-e1521195512702.jpeg

问题追踪

第一感觉是哪个正则匹配引起的,导致循环次数过多。在网上搜一下相关的问题,已经有人指出了,并且很容易复现,具体参考 https://stackoverflow.com/questions/36304204/%D0%A1-regex-segfault-on-long-sequences

那具体到自己的业务情况是怎样的呢?我们要找出有问题的正则,并且要修复。

出现 core 的情况下,错误日志还没有及时刷新到磁盘上,程序已经崩溃了。只能尽量查找出错的上下文。首先确定具体的 core 时间点,越精确越好,到秒基本就可以了。然后根据这个时间点 grep 具体机器上的日志。日志会打印入口请求及结束记录。如果找到断层的日志,也就是没有结束记录,那基本上可以断定在那个请求上发现的问题。确定具体请求后,结合相关业务,很容易找到问题出现的原因。

最后确定的正则是 .*。程序会做一层过滤,把书名号里面的文本进行过滤掉。确定原因后,我们可以复现下。

发现当 i=32697 时,程序出现了 core。

test += " "; 变成 test += "中";,发现当 i=10896,出现 core。

test += " ";变成 test += "《中》";, 发现当 i=3630 时,就会出现 core。

问题分析

首先来看下 .* 这个正则,严格的说,是错误的,语义并不明确。

这个正则的执行步骤是,首先匹配 ,如果匹配到,则执行 .* 的匹配,不管文本有多长,什么内容,是否包含 , 它都会一直往下匹配,直到文本最后。然后开始回溯,向前回溯一个字符,然后尝试匹配 ,如果找到,则匹配成功,完成。否则,会继续回溯,直到找到 ,如果没有找到,则匹配失败。

假设匹配的文本 《仙界科技》《灵魂导游》我是中国人,那么匹配的内容是 《仙界科技》《灵魂导游》,而不是单独的两个 《仙界科技》《灵魂导游》

解决问题

上面的正则,更准确的表述应该是 [^]+。但在C++中,对中文字符支持的很弱,导致某些匹配失败。

对于正则引擎来说,默认的规则是执行最长的匹配,通过特殊字符的使用,可以使用最短路径匹配。在我们的例子中就是 .*?。执行步骤是首先匹配 ,然后对下一个字符尝试匹配 ,如果匹配成功,则成功,否则匹配 .*, 这肯定能匹配到,然后再次尝试匹配 ,依次循环下去。

改正后的正则,再次执行相关用例,就不会出现 core 问题了(匹配过程中没有使用回溯)。

总结

std::regex_replace 确实有 bug 会引起 core 问题。没有统一的方式如何去规避。只能具体情况具体分析。保证两点基本上会规避这样的问题:

  • 匹配的内容不宜过长,很少正则需要匹配太长的内容(没有意义),向我们上面的例子,某些情况下,就是有 bug 的正则。 test += "《中》"; 这里的测试,就很容易复现。匹配内容过长。
  • 正则匹配的过程中,回溯的次数越少越好,更多的回溯,造成 core 的可能性越大。需要对正则做最坏情况的估计,保证最坏情况下,回溯越少越好。
此条目发表在C++分类目录,贴了, 标签。将固定链接加入收藏夹。

std::regex_replace 引起的core问题追踪》有 1 条评论

  1. 匿名说:

    遇到同样的问题,搞了好久,找到这里才解决

发表评论

邮箱地址不会被公开。