binmode + mod_perl 2.0.5 + Parse::RecDescent = 分段错误

Posted

技术标签:

【中文标题】binmode + mod_perl 2.0.5 + Parse::RecDescent = 分段错误【英文标题】:binmode + mod_perl 2.0.5 + Parse::RecDescent = segmentaion fault 【发布时间】:2013-09-03 05:17:32 【问题描述】:

重要更新:问题与 Apache 或 mod_perl 无关。 最简单的演示:

> perl -le 'use PerlIO::via::QuotedPrint; binmode(\*STDERR, ":via(PerlIO::via::QuotedPrint):utf8"); open (ERROR, ">&STDERR");'
zsh: segmentation fault  perl -le

事实上,binmode 由我的代码执行,open (ERROR, ">&STDERR"); 由 Parse::RecDescent 执行。


原问题:

我在 mod_perl 2.0.5 下的 Spreadsheet::WriteExcel 有问题 Apache 因分段错误而死,我发现它发生在 Spreadsheet::WriteExcel 包中的 require Parse::RecDescent 语句上。

strace 显示最后发生的事情是复制 STDERR:

[pid 31253] dup(2)                      = 8
[pid 31253] ioctl(8, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fffcf66a328) = -1 ENOTTY (Inappropriate ioctl for device)
[pid 31253] lseek(8, 0, SEEK_CUR)       = 0
[pid 31253] --- SIGSEGV (Segmentation fault) @ 0 (0) ---

我通读了Parse::RecDescent的代码,注意到open (ERROR, ">&STDERR");这样的语句

好吧,经过一些额外的实验,我有了这个简约的 Plack 应用程序来重现段错误:

use strict;
use warnings;

# DANGEROUS
use PerlIO::via::QuotedPrint;
binmode(\*STDERR, ":via(PerlIO::via::QuotedPrint):utf8");

my $app = sub 
    my $env = shift;

    open (ERROR, ">&STDERR");  # segmenatation fault

    return [
        '200',
        [ 'Content-Type' => 'text/plain' ],
        [ "hello world" ],
    ];
;

$app;

(其实我用的是PerlIO::via::QuotedPrint以外的binmode层,但效果是一样的)

如果我不执行binmode(\*STDERR, ":via(PerlIO...,则 apache 不会出现段错误。

如果我不复制 STDERR,则 apache 不会出现段错误。

如果我两者都做,则会出现段错误。

作为一种解决方法,我可以避免在 STDERR 上使用 binmode,但这并不好。

关于应该在哪里以及如何解决此问题的任何建议?

谢谢。

我的环境:

perl -v |grep version 
This is perl 5, version 14, subversion 2 (v5.14.2) built for x86_64-linux-gnu-thread-multi

uname -a
Linux thinkpad 3.2.0-32-generic #51-Ubuntu SMP Wed Sep 26 21:33:09 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

lsb_release -c
Codename:       precise

dpkg -l |grep mod-perl
ii  libapache2-mod-perl2  2.0.5-5ubuntu1   Integration of perl with the Apache2 web server

更新:相同的代码在过时的 Ubuntu 8.04 + perl 5.8.8 + mod_perl2 2.0.3 下运行良好

Upd2:FreeBSD 9.1 + perl 5.14 + mod_perl 2.0.8 -- 段错误重复

uname -a                              
FreeBSD liruoko.ru 9.1-RELEASE-p5 FreeBSD 9.1-RELEASE-p5 #7 r253740: Sun Jul 28 16:53:08 MSK 2013     roman@thor.cmc.msu.ru:/usr/obj/usr/src/sys/MINI  amd64

pkg info |grep apache                     
apache22-itk-mpm-2.2.25        Version 2.2.x of Apache web server with itk MPM.

pkg info |grep mod_perl               
ap22-mod_perl2-2.0.8,3         Embeds a Perl interpreter in the Apache2 server

perl -v |grep version
This is perl 5, version 14, subversion 4 (v5.14.4) built for amd64-freebsd

【问题讨论】:

提供更多信息。操作系统、Perl 发行版和版本是什么?万一软件过时了,升级了怎么办? Ubuntu 12.04 (LTS),perl 5.14,详细报告添加到问题 是的,Perl 和 mod_perl 从那时起都有几个版本,修复了一些崩溃错误。 mod_perl 2.0.8 和 perl 5.18.1 是否仍然会发生崩溃?如果是,请参阅perl.apache.org/docs/2.0/devel/debug/… 顺便说一句,如果我 降级(到 perl 5.8.8 + Ubuntu 8.04),最小示例和 Spreadsheet::WriteExcel 都可以正常工作 Inappropriate ioctl for device 让我想到某个地方的套接字或文件的权限。也许你的内核,apachemod_perlperl IO:: 层已经改变了它们处理这些的方式。 apachemod_perl 是从源代码编译的吗? 【参考方案1】:

如果它在没有设置binmode 的情况下工作,那么也许你有一个解决方案(如果不是对为什么会发生这种情况的真正答案)。 c.f.此摘录来自perldoc -f binmode

On some systems (in general, DOS- and Windows-based systems) binmode() is 
necessary when you're not working with a text file.  For the sake of portability 
it is a good idea always to use it when appropriate, and never to use it when it 
isn't appropriate.  Also, people can set their I/O to be by default UTF8-encoded 
Unicode, not bytes.

In other words: regardless of platform, use binmode() on binary data, like 
images, for example.   ...

perldoc 的独特风格中,我认为这可能暗示您可以为某些文件句柄/套接字设置binmode,而不是其他人调整,直到“错误”(如果它是一个) 不会出现。

编辑

感谢您简单且可重现的错误/测试用例,我认为这将得到解决。我构建了perl 的调试版本来尝试跟踪错误,它位于liberperl.so - 在PerlIOBase_dup() 的某个地方。我还在 IRC 上向可能知道的人提到了这一点,他们得出的结论是这是一个真实的(可报告的)perl 错误。

这是我运行gdb的方式:

(gdb) run -Dx -le 'use PerlIO::via::QuotedPrint; binmode(\*STDERR, 
":via(PerlIO::via::QuotedPrint):utf8"); open (ERROR,      ">&STDERR");'

事情就是这样结束的:

Program received signal SIGSEGV, Segmentation fault.
PerlIOBase_dup (f=0x0, o=0x801551060, param=0x0, flags=2) at perlio.c:2307 
2307 PerlIOBase(f)->flags |= PERLIO_F_UTF8;

干杯,你让perl 变得更好了!

【讨论】:

以上是关于binmode + mod_perl 2.0.5 + Parse::RecDescent = 分段错误的主要内容,如果未能解决你的问题,请参考以下文章

在 mod_perl 2 下关闭响应

使用 Apache 和 mod_perl 提供动态生成的图像的正确方法是啥?

在 mod_perl2 中修改 POST 请求

mod_perl 看不到 /tmp 中的文件

如何使用 mod_perl 发送自定义 http 状态码

如何在 OS X Yosemite 上的 Apache 2.4 中安装 mod_perl 2.0.9?