【问题标题】:Need to prevent PHP regex segfault需要防止 PHP regex segfault
【发布时间】:2009-11-12 14:11:18
【问题描述】:

为什么会出现以下段错误,我该如何预防?

<?php

$str = ' <fieldset> <label for="go-to">Go to: </label>  ' 
       . str_repeat(' ', 10000) 
       . '<input type="submit" value="Go" /> </fieldset> </form>';

preg_match_all("@
</?(?![bisa]\b)(?!em\b)[^>]*> # starting tag, must not be one of several inline tags
(?:[^<]|</?(?:(?:[bisau]|em|strong|sup)\b)[^>]*>)* #allow text and some inline tags
[\?\!\.]+
@ix", $str, $matches);

?>

我相信它会导致 .... 等待它 .... 堆栈溢出。

编辑:

以上是演示问题的模式的简化版本。更完整的版本:

@
</?(?![bisa]\b)(?!em\b)[^>]*> # starting tag, must not be one of several inline tags
(?:[^<]|</?(?:(?:[bisau]|em|strong|sup)\b)[^>]*>)* # continue, allow text content and some inline tags

# normal sentence ending
[\?\!\.]+ # valid ending characters -- note elipses allowed
(?<!\b[ap]m\.)(?<!\b[ap]\.m\.)(?<!digg this\!)(?<!Stumble This\!) # disallow some  false positives that we don't care about
\s*
(?:&apos;|&\#0*34;|'|&lsquo;)?\s* # closing single quotes, in the unusual case like "he said: 'go away'".
(?:"|&quot;|&\#0*34;|&\#x0*22;|&rdquo;|&\#0*8221;|&\#x0*201D;|''|``|\xe2\x80\x9d|&\#0*148;|&\#x0*94;|\x94|\))?\s* # followed by any kind of close-quote char
(?=\<) # should be followed by a tag.
@ix

目的是找到似乎以有效英文句子结尾结尾的 html 块。我发现这种方法非常擅长区分“内容”文本(如文章正文)和“布局”文本(即导航元素)。但是,有时如果标签之间有大量空白,它就会爆炸。

【问题讨论】:

  • 有趣 - 我也可以重现崩溃。我建议你解构正则表达式,取出元素直到它停止崩溃。然后看看您是否可以获取触发崩溃的元素并创建最简单的段错误示例,并将其记录在 bugs.php.net
  • 我认为这不是正则表达式引擎的错误。我只是认为你正在让它建立一个巨大的堆栈来处理回溯。也许如果你能解释你想要捕捉什么,我们可以建议一个替代正则表达式,减少回溯。
  • 如您所见,正则表达式并不是万能的工具。最好使用一个简单的解析器来读取你的标记结构,看看你有什么标签/元素。
  • ''有些人在遇到正则表达式时会想“我知道,我会使用我记得的朗朗上口的引语”。现在他们没有在讨论中添加任何内容。'' -- Tomalak

标签: php regex stack-overflow pcre


【解决方案1】:

我会尝试的第一件事是使所有量词都具有所有格,并使所有组原子:

"@</?+(?![bisa]\b)(?!em\b)[^>]*+>
(?>[^<]++|</?+(?>(?>[bisau]|em|strong|sup)\b)[^>]*+>)*+
[?!.]+
@ix"

我认为 Jeremy 是对的:不是回溯本身会杀死你,而是正则表达式引擎必须保存的所有状态信息才能使回溯成为可能。正则表达式的构造方式似乎是,如果它不得不回溯,无论如何它都会失败。所以使用所有格量词和原子组,不要费心保存所有无用的信息。

编辑:为了允许句子结尾的标点符号,您可以在第二行添加另一个替代方案:

(?>[^<?!.]++|(?![^?!.\s<]++<)[?!.]++|</?+(?>(?>[bisau]|em|strong|sup)\b)[^>]*+>)*+

添加匹配一个或多个所述字符,除非它们是元素中最后一个非空白字符。

【讨论】:

  • 我没有对其进行测试,但我认为你在这里一针见血(尽管在正则表达式的问题上同意你通常是安全的......)。
  • 我相信 PHP 的正则表达式(它是 PCRE 的某个版本)只支持原子分组,不支持所有格量​​词。我不相信您建议的模式会起作用,因为 [^
  • 另外,遗憾的是我不认为 PHP 的正则表达式允许可变长度的lookbehinds,否则我可以做(?&gt;(?:[^&lt;]|.....)*)(?&lt;=[?!.]+...)
  • PHP确实支持所有格量​​词。
  • 您对[^&lt;]++ 破坏了[?!.]+ 之前的所有内容有意见。尽管如果输入始终正确形成,这可能不是问题:即。在[?!.]+ 之前总是有一个&gt;
【解决方案2】:

我相当确定,即使是较新版本的 PHP 也与 PCRE 7.0 捆绑在一起,PCRE 7.0 具有已知的段错误问题。我不认为有任何纠正该问题的意图,因为它在技术上是 PCRE 问题,而不是 PHP 问题。

如果您告诉我们您正在尝试完成什么,您最好的选择是尝试编写一个替代表达式。

有问题的错误是:http://bugs.php.net/bug.php?id=40909

【讨论】:

    【解决方案3】:

    这还能做你想做的事吗?

    </?(?![bisa]\b)(?!em\b)[^>]*> # starting tag, must not be one of several inline tags
    (?:(?>[^<\?\!\.]*)|</?(?:(?:[bisau]|em|strong|sup)\b)[^>]*>)* #allow text and some inline tags
    [\?\!\.]+
    

    【讨论】:

    • 关闭,但不完全:这将不允许任何 [?!.] 在最后 [\?\!\.]+ 之前,但我想允许它们
    • 您介意提供一个此版本与您的不同匹配的测试用例吗?
    【解决方案4】:

    您的正则表达式会导致大量回溯。中间有 10000 个字符,它会变得非常混乱和缓慢。不过,我不希望它崩溃......!

    【讨论】:

    • 为什么不会崩溃?除了堆栈之外,如何处理回溯?当堆栈耗尽所有可用内存时会发生什么?
    • 我认为 pcre 的堆栈处理是固定大小的,实际上 IIRC 相对较小。
    • 我仍然不希望它崩溃。空间不足时失败,是的,但是程序崩溃?没有。
    • 递归由堆栈处理,这就是它溢出的原因。堆栈溢出空间不足(由操作系统处理,但可能存在安全漏洞)。
    • 如果你在 python 中做类似的事情,它不会出现段错误。请注意,它会挂起,但不会出现段错误。我认为 pcre 尝试自己的堆栈管理或类似的东西
    猜你喜欢
    • 2015-02-15
    • 2014-10-31
    • 2019-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-26
    • 1970-01-01
    • 2016-04-11
    相关资源
    最近更新 更多