为什么Elixir Regexes在达到一定长度时会破裂?

时间:2017-03-17 10:02:04

标签: regex erlang elixir

见这个例子:

str = "aaaaaaaaaaaaaaaaaaaaaa"
Regex.match? ~r/a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaaaaaaaa/, str
#=> true
Regex.match? ~r/a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaaaaaaaa/, str
#=> false

请注意,只有当我向正则表达式添加更多str时,如果a?变长,它就不会中断。

2 个答案:

答案 0 :(得分:5)

就像@Wiktor指出的那样,这个正则表达式导致一些Regex实现中的指数回溯,包括Erlang使用的一个(PCRE)。 match?返回普通false而不是抛出错误的原因是因为Erlang' :re.run/3如果默认达到此限制则会返回:nomatch

iex(1)> str = "aaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaa"
iex(2)> re = ~r/a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaaaaaaaa/
~r/a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaaaaaaaa/
iex(3)> Regex.match?(re, str)
false
iex(4)> :re.run(str, re.re_pattern)
:nomatch

:re.run/3可以通过传递:report_errors来报告此错误,以确认此Regex确实达到了限制:

iex(5)> :re.run(str, re.re_pattern, [:report_errors])
{:error, :match_limit}

来自文档:

  

以下是可能的运行时错误:

     

match_limit

     

PCRE库设置了限制次数   可以调用内部匹配函数。默认为10,000,000   为Erlang编译的库。如果返回{error,match_limit},则   正则表达式的执行已达到此限制。这是   通常被视为无匹配,这是默认的回报   发生这种情况时的值,但是通过指定report_errors,您就是   由于内部呼叫太多而导致比赛失败时通知。

可以增加限制(这通常是一个坏主意,因为正则表达式将非常慢并且如果用户提供正则表达式则可能导致DoS攻击):

iex(6)> :re.run(str, re.re_pattern, match_limit: 20_000_000)
{:match, [{0, 22}]}

答案 1 :(得分:1)

您的模式不起作用,因为所有模式都匹配在字符串cause catastrophical backtracking issue中的相同位置。

请注意,如果您要匹配0个或更多符号,请使用*量词:

str = "aaaaaaaaaaaaaaaaaaaaaa"
Regex.match? ~r/a*aaaaaaaaaaaaaaaaaaaaaa/, str
                ^^

a*将匹配0个或更多a个符号。请参阅online Elixir demo

详细了解catastrophical backtracking here