Stack corruption with Coro and AE::cv

Laurent laurent.aml at gmail.com
Wed May 27 00:59:30 CEST 2026


Hi,

Following is a test case which makes Perl > 5.22 crash.
I tested it on Perl 34, 38 and 42.

The corruptions usually segfault, or can generate scope errors like:
      perl: scope.c:1247: Perl_leave_scope: Assertion `!((((sv)->sv_flags &
(0x00004000|0x00008000)) == 0x00008000) && (((svtype)((sv)->sv_flags &
0xff)) == SVt_PVGV || ((svtype)((sv)->sv_flags & 0xff)) == SVt_PVLV))'
failed.
or ref errors like:
      Attempt to free unreferenced scalar: SV 0x55cc1a8d90 at
/usr/local/perl/lib/site_perl/5.38.2/aarch64-linux/Coro/AnyEvent.pm line
357.

And sometimes, the test passes.

The Coro/AnyEvent.pm line 357 is outline below:

sub Coro::AnyEvent::CondVar::_wait {
   until ($_[0]{_ae_sent}) {
      $AnyEvent::CondVar::Base::WAITING = 0; # avoid recursive check by AnyEvent
      local $_[0]{_ae_coro} = $Coro::current;
      Coro::schedule; # <<<<<< Line 357
   }
};

An AI suggestion is that this is due to the savestack being modified,
because of the $ref reference, so that unwinding the "local
$_[0]{_ae_coro}" at cancel time somehow fails to properly clean up
$_[0]{_ae_coro}, leaving it with an "unreferenced scalar".
It says the ref-cycle created by $_[0]{_ae_coro} = $Coro::current could
also be problematic.

The suggested fix is to not use local in this case (and to break the ref
cycle, but that's not strictly necessary):
sub Coro::AnyEvent::CondVar::_wait {
   my $self = shift;
   until ($self->{_ae_sent}) {
      $AnyEvent::CondVar::Base::WAITING = 0; # avoid recursive check by
AnyEvent

      # Prevent stack corruption with local+refcycles+strong ref on CV.
      # Set the coro reference manually (No 'local')
      # Weaken ensures there is no ref-cycle (coro->cv->coro).
      weaken($self->{_ae_coro} = $Coro::current);

      # Use a lexical guard to guarantee cleanup during exception unwinding
      scope_guard {
         delete $self->{_ae_coro};
      };

      Coro::schedule;
   }
};

At least, this fix lets the test pass.


The test follows:


$|=1;
print "1..10\n";

use EV;
use AnyEvent;
use Coro;
use Guard;

my $wakeup = AE::cv;
my $lock = AE::cv;
my $ref;
my $coro = async {
  my $guard = guard {
    print "ok 6 - guard called\n";
  };
  # Somehow, necessary to reproduce the problem.
  # We've seen refcnt errors as well... so maybe related.
  $ref = $wakeup;

  my $timer = AE::timer 1, 0, sub { $wakeup->(1); };
  print "ok 2 - coro waiting\n";
  $wakeup->recv;
  print "notok 10 - coro cancelled\n";
  exit 1;
};
$coro->on_destroy(sub {
  print "ok 7 - enter destroy\n";
  $lock->send;
  print "ok 8 - leave destroy\n";
});

print "ok 1 - coro created\n";

Coro::cede;
print "ok 3 - coro ready\n";

$coro->safe_cancel();
print "ok 4 - coro cancelled\n";

$coro->join();
print "ok 5 - coro joined\n";

# Segfault here.
$lock->recv;
print "ok 9 - coro destroyed notified\n";

$coro = undef;
print "ok 10 - coro freed\n";
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.schmorp.de/pipermail/anyevent/attachments/20260526/4f5b9fe0/attachment.htm>


More information about the anyevent mailing list