libstdc++的std::regex的一个坑

公司用Electron做的项目有个需求,前端的JS代码会把一张图片转成dataURL的格式传给cpp写的库,cpp把dataURL中的二进制数据提取出来通过自己的协议发给服务器。
用dataURL可以把文件的内容嵌入到URL字符串中。比如这段字符串复制到浏览器地址栏然后按回车,就可以看到一张红色的GIF图片。

data:image/gif;base64,R0lGODlhZABkAJEAAAAAAP////8AAP///yH5BAEAAAMALAAAAABkAGQAAAJzlI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8YhMKpfMpvMJjUqn1Kr1is1qt9yu9wsOi8fksvmMTqvX7Lb7DY/L5/S6/Y7P6/f8vv8PGCg4SFhoeIiYqLjI2Oj4CGlYAAA7

用DataURL的好处是较小的文件可以直接嵌入到HTML中。如果这个文件在网站中出现的次数很少,直接用dataURL就可以不用单独管理这个文件了。

所以就有个需求,判断给定的字符串是不是dataURL的类型。我想当然地就用了std::regex来判断字符串是不是dataURL,之后用std::regex_search来提取URL包含的MIME类型和包含的数据。写了这么一个正则表达式:

data:([a-zA-Z0-9/\+\-\.]*)(;(base64))?,(\S*)

当时没考虑到传过来的dataURL可能会很长,大小可能会有几个MB。之后QA说程序在linux下崩溃了,我挂上调试器按照同样的步骤重现了崩溃之后,看到了这么一个调用栈:

很明显regex_search在提取字符串的时候把调用栈弄爆了。查了libstdc++的std::regex里面的实现是用递归的,难怪容易爆栈。
https://www.zhihu.com/question/23070203/answer/84248248
改成只用regex解析文件头来判断就不崩溃了,dataURL里面包含的数据和URL头部的分界是个逗号,用第一个逗号字符出现的位置来分割应该没什么问题。

虽然程序崩了但是还是要膜这位实现出正则表达式引擎的Googler……

还有Visual Studio的linux调试器是个好东西。。。

Leave a Reply

Your email address will not be published. Required fields are marked *