MailScanner bug [Was: Re: SA 3.1.7 returning no result to MS?]

David Lee t.d.lee at
Fri Nov 17 16:09:15 GMT 2006

On Wed, 15 Nov 2006, David Lee wrote:

> [...]
> Looking a little deeper, it seems that in the "MailScanner/" module
> at the "eval { ...}" near line 800 (MS 4.56.8) all the results from:
>     $pipe->reader();
>     local $SIG{ALRM} = sub { die "Command Timed Out" };
>     alarm MailScanner::Config::Value('spamassassintimeout');
>     $SAHits = <$pipe>;
>     #print STDERR "Read SAHits = $SAHits " . scalar(localtime) . "\n";
>     $AutoLearn = <$pipe>;
>     $SAHitList = <$pipe>;
>     $SAReport  = <$pipe>;
>     #print STDERR "Read SAHitList = $SAHitList " . scalar(localtime) . "\n";
>     # Not sure if next 2 lines should be this way round...
>     waitpid $pid, 0;
>     $pipe->close();
>     $PipeReturn = $?;
> are coming back empty.  Hence the syslog entry:
>    Message ... is not spam, SpamAssassin (cached, score=0, required 6, autolearn=)
> This suggests that SA's expected behaviour would be that a 'good' return
> would always be accompanied by real, non-empty, data.  This, I presume, is
> what has always happened historically.
> But it now seems, after upgrade to SA 3.1.7, that SA occasionally provides
> empty data on a 'good' return: inconsistent.

I've probably slightly mis-read the detail there.  But the same principle
still applies.  I think we have an MS bug.

The "MailScanner/" code (my comments "##TDL##") does:

  my $pid = fork();

  if ($pid == 0) {
    # In the child              ##TDL## Child does the SpamAssassin call
      $spamness = $Test->check($mail);
    [ ... print SA results into $pipe ...]
    exit 0; # $SAResult;
                                ##TDL## Note: child has finished.
                                ##TDL## In MailScanner parent only.
  eval {
    [... read SA results ...]   ##TDL## from the SA call in child above.
    $PipeReturn = $?;
                                ##TDL## "$PipeReturn" never used

Now suppose that SA (from the fork's child) crashes.  Ideally, this
"shouldn't happen"(TM).  But we all know reality to be non-ideal, don't
we?  And in my own environment, from a different context, I have seen such
SA-3.1.7 crashes actually happen.  (If anyone is curious, I can provide
further info and core files.)

Then the parent (i.e. inside that "eval {}") will get empty data from SA,
and the parent will continue to run.  Worse, the existing code of MS fails
to distinguish whether the SA/child terminated cleanly (with real data
from SA/child/pipe) or crashed (no data from SA/child/pipe).

The parent stores a return code in "$PipeReturn".  Would that particular
code distinguish the crash from the normal termination?  Even if it does
potentially distinguish the various return types (success, fail, crash) of
the SA/child, the subsequent code doesn't ever use it (except merely
printing it as a low-priority debugging message 100 lines later).

I suggest therefore, that there is a MailScanner bug:

MS BUG: If MS's call to SA fails (e.g. crash/segfault), MS is unable to
distinguish this from a successful return.

RESOLUTION: The code immediately following the "eval {}" ought to check
the return (e.g "$PipeReturn") to detect SA/child failures.  Such failures
may reaonably be expected to be rare, but should nevertheless be handled,
perhaps by leaving the email where it is, and doing a high-priority (e.g.
"error") syslog message.

NOTE: Might the SA/child itself need an enclosing "eval {}"?


:  David Lee                                I.T. Service          :
:  Senior Systems Programmer                Computer Centre       :
:                                           Durham University     :
:            South Road            :
:                                           Durham DH1 3LE        :
:  Phone: +44 191 334 2752                  U.K.                  :

More information about the MailScanner mailing list