Perl 系统调用导致核心转储,但 $?保持为零

发布于 2024-09-13 05:38:35 字数 2783 浏览 9 评论 0 原文

我有一个 Perl 脚本(在 VirtualBox 内的 Xubuntu Lucid Lynx 上运行),它包装了多个 C/C++ 二进制文件,将其中一个的输入输入到另一个中。其中一行通常包含:

my $ret_code=`cat $input | c_binary`;
my $ret_val= $?;

对于某些输入文件,代码会导致 coredump,但 $ret_val$ret_code 分别为 0 和 ""。当我运行它时,我可以看到错误滚动,但我似乎无法以编程方式“捕获”这个错误。 我该怎么做? 目的是在出错时从输入中删除一些行并重试解析。

以下是错误:

*** stack smashing detected ***: code/parser terminated
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x50)[0x798390]
/lib/tls/i686/cmov/libc.so.6(+0xe233a)[0x79833a]
code/parser[0x804edd8]
[0x2e303039]
======= Memory map: ========
0043b000-0043c000 r-xp 00000000 00:00 0          [vdso]
0045a000-00475000 r-xp 00000000 08:01 11041      /lib/ld-2.11.1.so
00475000-00476000 r--p 0001a000 08:01 11041      /lib/ld-2.11.1.so
00476000-00477000 rw-p 0001b000 08:01 11041      /lib/ld-2.11.1.so
006b6000-00809000 r-xp 00000000 08:01 10897      /lib/tls/i686/cmov/libc-2.11.1.so
00809000-0080a000 ---p 00153000 08:01 10897      /lib/tls/i686/cmov/libc-2.11.1.so
0080a000-0080c000 r--p 00153000 08:01 10897      /lib/tls/i686/cmov/libc-2.11.1.so
0080c000-0080d000 rw-p 00155000 08:01 10897      /lib/tls/i686/cmov/libc-2.11.1.so
0080d000-00810000 rw-p 00000000 00:00 0 
008ba000-008d7000 r-xp 00000000 08:01 8268       /lib/libgcc_s.so.1
008d7000-008d8000 r--p 0001c000 08:01 8268       /lib/libgcc_s.so.1
008d8000-008d9000 rw-p 0001d000 08:01 8268       /lib/libgcc_s.so.1
00c89000-00cad000 r-xp 00000000 08:01 10901      /lib/tls/i686/cmov/libm-2.11.1.so
00cad000-00cae000 r--p 00023000 08:01 10901      /lib/tls/i686/cmov/libm-2.11.1.so
00cae000-00caf000 rw-p 00024000 08:01 10901      /lib/tls/i686/cmov/libm-2.11.1.so
08048000-08055000 r-xp 00000000 08:01 407893     /home/abugorsk/Documents/code/stepbystep/collins-parser/code/parser
08055000-08056000 r--p 0000c000 08:01 407893     /home/abugorsk/Documents/code/stepbystep/collins-parser/code/parser
08056000-08057000 rw-p 0000d000 08:01 407893     /home/abugorsk/Documents/code/stepbystep/collins-parser/code/parser
08057000-0c50f000 rw-p 00000000 00:00 0 
0e168000-0fa57000 rw-p 00000000 00:00 0          [heap]
b44a3000-b77c9000 rw-p 00000000 00:00 0 
b77da000-b77dc000 rw-p 00000000 00:00 0 
bff2b000-bff40000 rw-p 00000000 00:00 0          [stack]
Aborted

返回的值是:

LOG: Parser return code: 0    
LOG: Parser return value:

有问题的实际代码片段:

my $command = "cd $STEPBYSTEP_HOME/collins-parser; cat models/model$model_num/events | code/parser $src models/model$model_num/grammar 10000 1 1 1 1 1> $dest 2> $parse_log";
llog "Executing command: $command";
my $ret_code = $?;
llog "Parser return code: $ret_code";
my $ret_val = `$command`;

I've got a Perl script (running on Xubuntu Lucid Lynx within VirtualBox) that wraps around several C/C++ binaries feeding the inputs of one into the others. One of the lines consists of generally:

my $ret_code=`cat $input | c_binary`;
my $ret_val= $?;

For some input files the code causes a coredump, but both $ret_val and $ret_code are 0 and "" respectively. I can see the errors scrolling by when I run it, but I seem to have no way of "capturing" this programmatically. How could I do this? The intent is to on error remove some lines from the input and retry the parsing.

Here are the errors:

*** stack smashing detected ***: code/parser terminated
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x50)[0x798390]
/lib/tls/i686/cmov/libc.so.6(+0xe233a)[0x79833a]
code/parser[0x804edd8]
[0x2e303039]
======= Memory map: ========
0043b000-0043c000 r-xp 00000000 00:00 0          [vdso]
0045a000-00475000 r-xp 00000000 08:01 11041      /lib/ld-2.11.1.so
00475000-00476000 r--p 0001a000 08:01 11041      /lib/ld-2.11.1.so
00476000-00477000 rw-p 0001b000 08:01 11041      /lib/ld-2.11.1.so
006b6000-00809000 r-xp 00000000 08:01 10897      /lib/tls/i686/cmov/libc-2.11.1.so
00809000-0080a000 ---p 00153000 08:01 10897      /lib/tls/i686/cmov/libc-2.11.1.so
0080a000-0080c000 r--p 00153000 08:01 10897      /lib/tls/i686/cmov/libc-2.11.1.so
0080c000-0080d000 rw-p 00155000 08:01 10897      /lib/tls/i686/cmov/libc-2.11.1.so
0080d000-00810000 rw-p 00000000 00:00 0 
008ba000-008d7000 r-xp 00000000 08:01 8268       /lib/libgcc_s.so.1
008d7000-008d8000 r--p 0001c000 08:01 8268       /lib/libgcc_s.so.1
008d8000-008d9000 rw-p 0001d000 08:01 8268       /lib/libgcc_s.so.1
00c89000-00cad000 r-xp 00000000 08:01 10901      /lib/tls/i686/cmov/libm-2.11.1.so
00cad000-00cae000 r--p 00023000 08:01 10901      /lib/tls/i686/cmov/libm-2.11.1.so
00cae000-00caf000 rw-p 00024000 08:01 10901      /lib/tls/i686/cmov/libm-2.11.1.so
08048000-08055000 r-xp 00000000 08:01 407893     /home/abugorsk/Documents/code/stepbystep/collins-parser/code/parser
08055000-08056000 r--p 0000c000 08:01 407893     /home/abugorsk/Documents/code/stepbystep/collins-parser/code/parser
08056000-08057000 rw-p 0000d000 08:01 407893     /home/abugorsk/Documents/code/stepbystep/collins-parser/code/parser
08057000-0c50f000 rw-p 00000000 00:00 0 
0e168000-0fa57000 rw-p 00000000 00:00 0          [heap]
b44a3000-b77c9000 rw-p 00000000 00:00 0 
b77da000-b77dc000 rw-p 00000000 00:00 0 
bff2b000-bff40000 rw-p 00000000 00:00 0          [stack]
Aborted

The values returned are:

LOG: Parser return code: 0    
LOG: Parser return value:

The actual code snippet in question:

my $command = "cd $STEPBYSTEP_HOME/collins-parser; cat models/model$model_num/events | code/parser $src models/model$model_num/grammar 10000 1 1 1 1 1> $dest 2> $parse_log";
llog "Executing command: $command";
my $ret_code = $?;
llog "Parser return code: $ret_code";
my $ret_val = `$command`;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

你的背包 2024-09-20 05:38:35

首先,您显示的代码中有一些可疑之处:您在实际运行命令之前获取了 $? 的值。现在我将讨论我认为您要写的内容:

my $command = "cd $STEPBYSTEP_HOME/collins-parser;" .
              "cat models/model$model_num/events | code/parser $src models/model$model_num/grammar 10000 1 1 1 1 1> $dest 2> $parse_log";
my $ret_val = `$command`;
my $ret_code = $?;

在此之后,$ret_code 包含整个 shell 命令的状态。这又是列表中最后一个命令的状态,它是一个管道 cat ... |代码/解析器...。根据 shell 的不同,这可能是管道中最后一个命令的状态,即 code/parser(ksh、zsh),或者始终为 0(大多数 shell,包括 ash、bash 和pdksh)。

对于您的情况,有一个简单的修复方法,即摆脱 cat 的无用使用:

my $command = "cd $STEPBYSTEP_HOME/collins-parser &&" .
              "<models/model$model_num/events code/parser $src models/model$model_num/grammar 10000 1 1 1 1 1> $dest 2> $parse_log";
my $ret_val = `$command`;
my $ret_code = $?;

如果您有一个有用的命令而不是 cat,那么您最好的选择是完全放弃外壳。这还有其他一些小好处:少了一个需要掌握的工具;更容易移植到非 UNIX 系统;适用于包含 shell 元字符的文件名(这也可以通过系统地使用 quotemeta 来实现)。这是这个想法的要点(未经测试); perldoc -f openperldoc perlipc 可能会有所帮助。

use File::Slurp;
if (open my $fh, "|-") {
    # Parent code
    my $ret_val = read_file($fh);
    close($ret_code);
    my $ret_code = $?;
    ...
} else { # Child code
    chdir "$ENV{STEPBYSTEP_HOME}/collins-parser" or die $!;
    open STDIN, "<", "models/model$model_num/events" or die $!;
    open STDOUT, ">", $dest or die $!;
    open STDERR, ">", $parse_log or die $!;
    exec "code/parser", $src, "models/model$model_num/grammar", "1", "1", "1", "1", "1";
    die $!;
}

First, there's something fishy in the code you show: you're getting the value of $? before actually running the command. I'll now discuss what I think you meant to write:

my $command = "cd $STEPBYSTEP_HOME/collins-parser;" .
              "cat models/model$model_num/events | code/parser $src models/model$model_num/grammar 10000 1 1 1 1 1> $dest 2> $parse_log";
my $ret_val = `$command`;
my $ret_code = $?;

After this, $ret_code contains the status of the whole shell command. This, in turn, is the status of the last command in the list, which is a pipeline cat ... | code/parser .... Depending on the shell, this may either be the status of the last command in the pipeline, i.e., code/parser (ksh, zsh), or always be 0 (most shells, including ash, bash and pdksh).

In your case, there's a simple fix, which is to get rid of the useless use of cat:

my $command = "cd $STEPBYSTEP_HOME/collins-parser &&" .
              "<models/model$model_num/events code/parser $src models/model$model_num/grammar 10000 1 1 1 1 1> $dest 2> $parse_log";
my $ret_val = `$command`;
my $ret_code = $?;

If you had a useful command instead of cat, your best option would be to dispense with the shell altogether. This also has other minor benefits: one less tool to master; easier to port to non-unix systems; works with file names containing shell metacharacters (this can also be achieved by systematic use of quotemeta). Here's the gist of the idea (untested); perldoc -f open and perldoc perlipc may help.

use File::Slurp;
if (open my $fh, "|-") {
    # Parent code
    my $ret_val = read_file($fh);
    close($ret_code);
    my $ret_code = $?;
    ...
} else { # Child code
    chdir "$ENV{STEPBYSTEP_HOME}/collins-parser" or die $!;
    open STDIN, "<", "models/model$model_num/events" or die $!;
    open STDOUT, ">", $dest or die $!;
    open STDERR, ">", $parse_log or die $!;
    exec "code/parser", $src, "models/model$model_num/grammar", "1", "1", "1", "1", "1";
    die $!;
}
GRAY°灰色天空 2024-09-20 05:38:35

首先,您的命令行中有一个无用的 cat,可以轻松地通过重定向替换它。

我会尝试将命令更改为类似以下内容

my $command = "cd $STEPBYSTEP_HOME/collins-parser && code/parser $src models/model$model_num/grammar 10000 1 1 1 1 < models/model$model_num/events 1> $dest 2> $parse_log";

或者,如果您尝试最小化输入文件以查找导致崩溃的原因,我强烈建议使用 Delta,它可以有效地自动执行此操作

First, you've got a useless cat in your command line that could easily be replaced by a redirection.

I'd try changing the command to something like the following

my $command = "cd $STEPBYSTEP_HOME/collins-parser && code/parser $src models/model$model_num/grammar 10000 1 1 1 1 < models/model$model_num/events 1> $dest 2> $parse_log";

Alternately, if you're trying to minimize your input file to find whatever's causing the crash, I highly recommend using Delta, which automates this effectively

别想她 2024-09-20 05:38:35

由于 CRT 正在中止程序(即它实际上并没有通过信号崩溃,CRT 看到了废弃的堆栈金丝雀并手动中止了进程),因此其返回值将为零。我认为你在这里能做的最好的事情就是:

`cat $input | c_binary 2>&1`

这样 CRT 垃圾就会被捕获,并且你可以在 Perl 脚本中检测到它。

Since the CRT is aborting the program (i.e. it doesn't actually crash via a signal, the CRT saw the trashed stack canary and manually aborted the process), its return value will be zero. I think the best you can do here is:

`cat $input | c_binary 2>&1`

so that the CRT gunk will be captured and you can detect it in the Perl script.

∝单色的世界 2024-09-20 05:38:35

为您的 c_binary 编译这个简单的替代品

#include <string.h>
void f(void)
{
  char smallbuf[9];
  strcpy(smallbuf, "dy-no-MITE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
int main(void)
{
  f();
  return 0;
}

和这个 Perl 程序来运行它的图像

#! /usr/bin/perl

use warnings;
use strict;

use POSIX;

if (system("./c_binary") == 0) {
  print "$0: c_binary exited normally\n";
}
else {
  warn "$0: c_binary exited ", ($? >> 8), "\n",
       WIFSIGNALED($?)
         ? ("  - terminated by signal ", WTERMSIG($?), "\n") : ();
}

我得到

$ ./boom

*** stack smashing detected ***: ./c_binary terminated
./prog.pl: c_binary exited 0
  - terminated by signal 11

所以如您所见,您需要使用 WIFSIGNALEDWTERMSIG< /code> 来自 POSIX 模块,以编程方式检测 c_binary 被信号杀死——不仅仅是退出状态本身:

WIF信号已发送

WIFSIGNALED($?) 如果子进程因信号而终止,则返回 true

WTERMSIG

WTERMSIG($?) 返回子进程终止的信号(仅当 WIFSIGNALED($?) 为 true 时才有意义)

Compiling this simple stand-in for your c_binary

#include <string.h>
void f(void)
{
  char smallbuf[9];
  strcpy(smallbuf, "dy-no-MITE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
int main(void)
{
  f();
  return 0;
}

and this Perl program to run its image

#! /usr/bin/perl

use warnings;
use strict;

use POSIX;

if (system("./c_binary") == 0) {
  print "$0: c_binary exited normally\n";
}
else {
  warn "$0: c_binary exited ", ($? >> 8), "\n",
       WIFSIGNALED($?)
         ? ("  - terminated by signal ", WTERMSIG($?), "\n") : ();
}

I get

$ ./boom

*** stack smashing detected ***: ./c_binary terminated
./prog.pl: c_binary exited 0
  - terminated by signal 11

So as you can see, you need to use WIFSIGNALED and WTERMSIG from the POSIX module to detect programmatically that c_binary was killed by a signal—not just the exit status itself:

WIFSIGNALED

WIFSIGNALED($?) returns true if the child process terminated because of a signal

WTERMSIG

WTERMSIG($?) returns the signal the child process terminated for (only meaningful if WIFSIGNALED($?) is true)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文