Released stashes could cause SEGV

(2010/11/22 追記: 5.12ではこの問題は解決され、SEGVはおきなくなっている。)
Released CvGVs could cause SEGVの注で「シンボルテーブル <-> GV間にも循環参照があるため同じ問題を抱えるのだが,こちらは真のウィークリファレンスを導入して解決した」と書いたが、よく調べてみると解決しているとは言いがたい状態だった。

まず、スタッシュ(Symbol Table Hash)とGV(type glob)の関係だが、スタッシュはGVを「所有」している*1。一方、GVはフィールドに所属しているスタッシュを持ち、CレベルではGvSTASH(gv)として参照できるのだが、このGVからスタッシュへの関係はウィークリファレンスとなっている。したがって、不要なGVやスタッシュは適切に解放される。しかし、このデータ構造は、GVが存在しつつ所属元のスタッシュが解放されるという事が起こり得る*2
以下のスクリプトはそのような状況を作り出す。

#!perl
use strict;
use Devel::Peek;

my $gv = *Foo::bar;
delete $::{'Foo::'}; # 所属元のスタッシュを削除する

Dump $gv;
__END__

結果:

SV = PVGV(0x1004de68) at 0x1004f668
  REFCNT = 1
  FLAGS = (PADMY,FAKE,MULTI,IMPORTALL)
  NAME = "bar"
  NAMELEN = 3
  GvSTASH = 0x0
  GP = 0x10067b50
    SV = 0x0
    REFCNT = 2
    IO = 0x0
    FORM = 0x0  
    AV = 0x0
    HV = 0x0
    CV = 0x0
    CVGEN = 0x0
    LINE = 6
    FILE = "stash.pl"
    FLAGS = 0xf2
    EGV = 0x1004f710	"bar"

このように GvSTASH = 0x0 となっており、ウィークリファレンスメカニズムが適切に働いていることが分かる。
しかし、このGVはデータとしては壊れており、何か操作を加えるとすぐSEGVする。

#!perl
use strict;
use Devel::Peek;

my $gv = *Foo::bar;
delete $::{'Foo::'};
print $gv;   # SEGV
Dump($gv);
__END__

これはGvSTASHを参照している箇所でNULLかどうかをチェックすればよいので、問題の解決はそれほど困難でなないと思われる。
よって[あとで報告する]。

*1:つまり、リファレンスカウントについて責任を負っている。

*2:先のCvGVの問題も同じ構造である。