c++ - 正则表达式分段错误
问题描述
我有一个触发segmentation fault
错误的正则表达式。经过一些测试后,我注意到[\s\S]*\s+
如果字符串大于 15 KB,则正则表达式的一部分会出现问题,所以有时它可以工作,但有时它会崩溃。
这是用 g++ (gcc v. 6.3.0) 编译的 C++ 代码
#include <regex>
#include <fstream>
#include <string>
#include <iostream>
int main (int argc, char *argv[]) {
std::regex regex(
R"([\s\S]*\s+)",
std::regex_constants::icase
);
std::ifstream ifs("/home/input.txt");
const std::string input(
(std::istreambuf_iterator<char>(ifs)),
(std::istreambuf_iterator<char>())
);
std::cout << "input size: " << input.size() << std::endl;
bool reg_match = std::regex_match(input, regex);
std::cout << "matched: " << reg_match << std::endl;
}
发生了什么,为什么会发生这种模式以及为什么会受到输入大小的影响?
更新:
使用 -fsanitize=address 编译时运行二进制文件产生的错误:
g++ -std=c++11 /home/app/src/test.cpp -o /home/app/bin/test -fsanitize=address
ASAN:DEADLYSIGNAL
=================================================================
==37041==ERROR: AddressSanitizer: stack-overflow on address 0x7ffbff8edff8 (pc 0x55afae25781b bp 0x7ffbff8ee010 sp 0x7ffbff8edff0 T0)
#0 0x55afae25781a in bool __gnu_cxx::operator==<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&, __gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > const&) (/home/app/bin/test+0x1981a)
#1 0x55afae2587bd in std::__detail::_Executor<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::__cxx11::sub_match<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::__cxx11::regex_traits<char>, true>::_M_dfs(std::__detail::_Executor<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::__cxx11::sub_match<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::__cxx11::regex_traits<char>, true>::_Match_mode, long) (/home/app/bin/test+0x1a7bd)
#2 0x55afae25e2d2 in std::__detail::_Executor<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::__cxx11::sub_match<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::__cxx11::regex_traits<char>, true>::_M_rep_once_more(std::__detail::_Executor<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::__cxx11::sub_match<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::__cxx11::regex_traits<char>, true>::_Match_mode, long) (/home/app/bin/test+0x202d2)
.
.
.
#251 0x55afae25e2d2 in std::__detail::_Executor<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::__cxx11::sub_match<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::__cxx11::regex_traits<char>, true>::_M_rep_once_more(std::__detail::_Executor<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::__cxx11::sub_match<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::__cxx11::regex_traits<char>, true>::_Match_mode, long) (/home/app/bin/test+0x202d2)
SUMMARY: AddressSanitizer: stack-overflow (/home/app/bin/test) in std::__detail::_Executor<__gnu_cxx::__normal_iterator<char const*, std:__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::__cxx11::sub_match<__gnu_cxx::__normal_iterator<char const*,std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::__cxx11::regex_traits<char>, true>::_M_dfs(std::__detail::_Executo<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::__cxx11::submatch<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::__cxx11::regex_trats<char>, true>::_Match_mode, long)
==37017==ABORTING
解决方案
我没有完整的答案,但由于某种原因,在匹配您的正则表达式时会发生堆栈溢出。这通常是由于堆栈上的数据过多或递归级别过多造成的。查看您的程序,我在堆栈上看不到任何大对象(堆栈上的字符串对象很小,因为它的数据在堆上)。但是,用于正则表达式解析的状态机以进行许多递归函数调用而闻名(这与您的长 Address Sanitizer 输出一起使用)。您有几个选择(我会按此顺序尝试):
- 确保您使用适当的优化级别进行编译。在更高级别的优化编译器通常会在堆栈上推送更少的帧(请参阅“尾调用优化”)。
- 尝试传递
std::regex_constants::optimize
以鼓励正则表达式代码在构建用于正则表达式处理的状态机时花费更多时间进行优化。 - 重新考虑你的正则表达式。也许您可以简化它,从而使状态机更简单,递归级别更少?这
[\s\S]*
部分看起来有点不合常规。 - 修改您的程序以处理较小块中的输入数据,例如逐行处理。
- 增加你的筹码量。
ulimit -s <stack size in kB>
在 POSIX 系统上,您可以通过在运行程序之前调用来做到这一点。 - 考虑使用自动增长的堆栈 (
-fsplit-stacks
)来编译您的程序。请注意,这是以牺牲一些性能为代价的。
推荐阅读
- font-awesome - 如何离线使用 Font Awesome?
- gdb - 获取 GDB 中函数的参数名称而不遇到断点
- python - 如果它的循环小于数字 n,这个 python 循环如何检查素数?
- list - 如何将列表中的连续数字组合到 Haskell 中的范围中?
- vb.net - 更改组合框中的内容时如何清除文本框?
- c# - 如何在 c# winforms 中为按钮添加货币价值?
- python - 在从 n 开始的多级索引 pandas 数据帧循环中,然后在整个数据帧上从 n+1、n+2、nth 开始再次循环
- genexus - 很容易意外关闭“保存时应用此模式”。有什么办法让这更困难或至少添加一个确认?
- javascript - 错误通过测试它不应该在 nrgx 选择器测试
- git - .gitignore 文件不起作用,git status 仍然显示 .gitignore 中列出的文件(已解决)