代码之家  ›  专栏  ›  技术社区  ›  NuclearDog

php的preg-match_都会导致apache segfault

  •  3
  • NuclearDog  · 技术社区  · 15 年前

    我使用两个正则表达式从MySQL查询中提取分配,并使用它们创建审计跟踪。其中一个是“挑剔的”,需要引用列名等,另一个没有。

    这两种方法都经过测试,并正确地解析出值。我遇到的问题是,在某些查询中,“picky”regexp实际上只是导致Apache出错。

    我尝试了很多方法来确定这是导致代码中保留regexp的原因,只是修改条件以确保它没有运行(排除某种编译时问题或其他问题)。没有问题。只有当它针对特定的查询运行regexp时,它才会出错,我找不到任何明显的模式来告诉我原因。

    相关代码:

    if ($picky)
        preg_match_all("/[`'\"]((?:[A-Z]|[a-z]|_|[0-9])+)[`'\"] *= *'((?:[^'\\\\]|\\\\.)*)'/", $sql, $matches);
    else
        preg_match_all("/[`'\"]?((?:[A-Z]|[a-z]|_|[0-9])+)[`'\"]? *= *[`'\"]?([^`'\" ,]+)[`'\"]?/", $sql, $matches);
    

    这两种方法的唯一区别是,第一种方法删除了引号上的问号,使它们成为非可选的,并删除了对值使用不同类型引号的选项-只允许使用单引号。将第一个regexp替换为第二个regexp(用于测试目的)并使用相同的数据将消除此问题-它是 一定地 与regexp有关。

    导致我悲伤的特定SQL可从以下网址获得:
    http://stackoverflow.pastebin.com/m75c2a2a0

    有趣的是,当我删除突出显示的部分时,它都可以正常工作。尝试自己提交突出显示的部分不会导致错误。

    我对这里发生的事很困惑。有人能就进一步的调试或修复提供任何建议吗?

    编辑:没有什么特别令人兴奋的,但是为了完整起见,这里有来自apache的相关日志条目(/var/log/apache2/error.log-站点的error.log中没有任何内容。在访问日志中甚至没有提到请求。)

    [Thu Dec 10 10:08:03 2009] [notice] child pid 20835 exit signal Segmentation fault (11)
    

    其中一个用于包含该查询的每个请求。

    伊迪丝2:根据库尔基·卡兹的建议,我试了同样长度的胡言乱语,得到了同样的错误。坐下来,试了一堆不同的长度,找到了极限。6035个字符可以。6036个分段。

    edit3:更改 pcre.backtrack_limit pcre.recursion_limit 在里面 php.ini 在一定程度上缓解了这个问题。Apache不再是segFaults,但我的regexp不再匹配字符串中的所有匹配项。显然,这是一个长期以来(从2007年起)在php/pcre中已知的错误:
    http://bugs.php.net/bug.php?id=40909

    edit4:我在下面的答案中发布了代码,我用它来替换这个特定的正则表达式,因为解决方法不适合我的目的(待售产品,不能保证php.in i的更改,regexp只能部分地工作,删除了我们需要的功能)。我发布的代码被发布到公共领域,没有任何形式的保证或支持。我希望它能帮助别人。:)

    谢谢大家的帮助!

    亚当

    3 回复  |  直到 15 年前
        1
  •  4
  •   NDP    15 年前

    我也遇到过类似的preg_匹配相关问题,同样的apache segfault。只有导致它的preg_匹配是内置在我使用的CMS(wordpress)中的。

    提供的“解决方法”是在php.ini中更改这些设置:

    [PCRE ] ;PCRE库回溯限制。 ;pcre.backtrack_限制=100000 pcre.recursion_limit=200000000 pcre.backtrack_限制=100000000

    权衡是为了呈现更大的页面(在我的例子中,>200行;当其中一列被限制为1500个字符的文本描述时),您将获得相当高的CPU利用率,而我仍然看到segfaults。只是不经常。

    我的网站接近生命的尽头,所以我没有太多的需求(或预算)来寻找真正的解决方案。但也许这可以缓解你所看到的问题。

        2
  •  4
  •   Kuroki Kaze    15 年前

    有趣的是,当我删除突出显示的部分时,它都可以正常工作。尝试自己提交突出显示的部分不会导致错误。

    提交文件的大小如何?如果你通过等长的胡言乱语,会发生什么?

    编辑:拆分和合并将如下所示:

    $strings = explode("\n", $sql);
    
    $matches = array(array(), array(), array());
    
    foreach ($strings AS $string) {
        preg_match_all("/[`'\"]?((?:[A-Z]|[a-z]|_|[0-9])+)[`'\"]? *= *[`'\"]?([^`'\" ,]+)[`'\"]?/", $string, $matches_temp);
        $matches[0] = array_merge($matches[0], $matches_temp[0]);
        $matches[1] = array_merge($matches[1], $matches_temp[1]);
        $matches[2] = array_merge($matches[2], $matches_temp[2]);
    }
    
        3
  •  1
  •   NuclearDog    15 年前

    考虑到在保存页面或执行其他不经常执行的操作时,这只需要与查询匹配,我认为以下代码的性能冲击是可以接受的。它解析SQL查询( $sql )并将name=>值对放入 $data . 似乎工作得很好,处理大型查询也很好。

                $quoted = '';
                $escaped = false;
    
                $key = '';
                $value = '';
                $target = 'key';
    
                for ($i=0; $i<strlen($sql); $i++)
                {
                    if ($escaped)
                    {
                        $$target .= $sql[$i];
                        $escaped = false;
                    }
                    else if ($quoted!='')
                    {
                        if ($sql[$i]=='\\')
                            $escaped = true;
                        else if ($sql[$i]==$quoted)
                            $quoted = '';
                        else
                            $$target .= $sql[$i];
                    }
                    else
                    {
                        if ($sql[$i]=='\'' || $sql[$i]=='`')
                        {
                            $quoted = $sql[$i];
                            $$target = '';
                        }
                        else if ($sql[$i]=='=')
                            $target = 'value';
                        else if ($sql[$i]==',')
                        {
                            $target = 'key';
                            $data[$key] = $value;
                            $key = '';
                            $value = '';
                        }
                    }
                }
    
                if ($value!='')
                    $data[$key] = $value;
    

    感谢大家的帮助和指导!