Skip to content

Commit 05fb844

Browse files
committed
Two bugfixes to field handling during thread cloning
This fixes two separate but related bugs. * Thread clone crashed if any class existed with no fields in it. This is fixed by permitting a NULL PADNAMELIST parameter to padnamelist_dup(). [GH23771] * Thread cloning would unreliably segfault due to missing PadnameFIELDINFO() of an outer closure capture, depending on the exact order of CV discovery. This is fixed by correct usage of the PL_ptr_table to store details of cloned `struct padname_fieldinfo` structures, and careful ordering of assignments and recursive clone calls.
1 parent 162b6fd commit 05fb844

File tree

5 files changed

+33
-13
lines changed

5 files changed

+33
-13
lines changed

embed.fnc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6425,7 +6425,7 @@ Rdp |PADLIST *|padlist_dup |NN PADLIST *srcpad \
64256425
Rdp |PADNAME *|padname_dup |NN PADNAME *src \
64266426
|NN CLONE_PARAMS *param
64276427
Rdp |PADNAMELIST *|padnamelist_dup \
6428-
|NN PADNAMELIST *srcpad \
6428+
|NULLOK PADNAMELIST *srcpad \
64296429
|NN CLONE_PARAMS *param
64306430
Cp |yy_parser *|parser_dup |NULLOK const yy_parser * const proto \
64316431
|NN CLONE_PARAMS * const param

inline.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4681,7 +4681,8 @@ Perl_padname_refcnt_inc(PADNAME *pn)
46814681
PERL_STATIC_INLINE PADNAMELIST *
46824682
Perl_padnamelist_refcnt_inc(PADNAMELIST *pnl)
46834683
{
4684-
PadnamelistREFCNT(pnl)++;
4684+
if (pnl)
4685+
PadnamelistREFCNT(pnl)++;
46854686
return pnl;
46864687
}
46874688

pad.c

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,6 +1295,7 @@ S_pad_findlex(pTHX_ const char *namepv, STRLEN namelen, U32 flags, const CV* cv,
12951295
return NOT_IN_PAD;
12961296

12971297
if (PadnameIsFIELD(*out_name)) {
1298+
assert(PadnameFIELDINFO(*out_name));
12981299
HV *fieldstash = PadnameFIELDINFO(*out_name)->fieldstash;
12991300

13001301
/* fields are only visible to the class that declared them */
@@ -2746,6 +2747,9 @@ Perl_padnamelist_dup(pTHX_ PADNAMELIST *srcpad, CLONE_PARAMS *param)
27462747
{
27472748
PERL_ARGS_ASSERT_PADNAMELIST_DUP;
27482749

2750+
if (!srcpad)
2751+
return NULL;
2752+
27492753
SSize_t max = PadnamelistMAX(srcpad);
27502754

27512755
/* look for it in the table first */
@@ -2826,6 +2830,7 @@ Perl_newPADNAMEouter(PADNAME *outer)
28262830
PadnameREFCNT_inc(PADNAME_FROM_PV(PadnamePV(outer)));
28272831
PadnameFLAGS(pn) = PADNAMEf_OUTER;
28282832
if(PadnameIsFIELD(outer)) {
2833+
assert(PadnameFIELDINFO(outer));
28292834
PadnameFIELDINFO(pn) = PadnameFIELDINFO(outer);
28302835
PadnameFIELDINFO(pn)->refcount++;
28312836
PadnameFLAGS(pn) |= PADNAMEf_FIELD;
@@ -2848,6 +2853,7 @@ Perl_padname_free(pTHX_ PADNAME *pn)
28482853
if (PadnameOUTER(pn))
28492854
PadnameREFCNT_dec(PADNAME_FROM_PV(PadnamePV(pn)));
28502855
if (PadnameIsFIELD(pn)) {
2856+
assert(PadnameFIELDINFO(pn));
28512857
struct padname_fieldinfo *info = PadnameFIELDINFO(pn);
28522858
if(!--info->refcount) {
28532859
SvREFCNT_dec(info->fieldstash);
@@ -2899,18 +2905,27 @@ Perl_padname_dup(pTHX_ PADNAME *src, CLONE_PARAMS *param)
28992905
PadnameTYPE (dst) = (HV *)sv_dup_inc((SV *)PadnameTYPE(src), param);
29002906
PadnameOURSTASH(dst) = (HV *)sv_dup_inc((SV *)PadnameOURSTASH(src),
29012907
param);
2902-
if(PadnameIsFIELD(src) && !PadnameOUTER(src)) {
2908+
if(PadnameIsFIELD(src)) {
2909+
assert(PadnameFIELDINFO(src));
29032910
struct padname_fieldinfo *sinfo = PadnameFIELDINFO(src);
2904-
struct padname_fieldinfo *dinfo;
2905-
Newxz(dinfo, 1, struct padname_fieldinfo);
2906-
2907-
dinfo->refcount = 1;
2908-
dinfo->fieldix = sinfo->fieldix;
2909-
dinfo->fieldstash = hv_dup_inc(sinfo->fieldstash, param);
2910-
dinfo->paramname = sv_dup_inc(sinfo->paramname, param);
2911-
2912-
PadnameFIELDINFO(dst) = dinfo;
2911+
struct padname_fieldinfo *dinfo = (struct padname_fieldinfo *)ptr_table_fetch(PL_ptr_table, src);
2912+
if (dinfo)
2913+
PadnameFIELDINFO(dst) = dinfo;
2914+
else {
2915+
Newxz(dinfo, 1, struct padname_fieldinfo);
2916+
PadnameFIELDINFO(dst) = dinfo;
2917+
ptr_table_store(PL_ptr_table, sinfo, dinfo);
2918+
2919+
/* We must have set PadnameFIELDINFO(dst) before we recurse into
2920+
* fieldstash in case it points back here */
2921+
dinfo->refcount = 1;
2922+
dinfo->fieldix = sinfo->fieldix;
2923+
dinfo->fieldstash = hv_dup_inc(sinfo->fieldstash, param);
2924+
dinfo->paramname = sv_dup_inc(sinfo->paramname, param);
2925+
}
2926+
assert(PadnameFIELDINFO(dst));
29132927
}
2928+
29142929
dst->xpadn_low = src->xpadn_low;
29152930
dst->xpadn_high = src->xpadn_high;
29162931
dst->xpadn_gen = src->xpadn_gen;

proto.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

t/class/threads.t

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ class Testcase1 {
2121
method x { return $x }
2222
}
2323

24+
class WithNoFields {
25+
# a class with no fields, in order to test [GH23771]
26+
}
27+
2428
{
2529
my $ret = threads->create(sub {
2630
pass("Created dummy thread");

0 commit comments

Comments
 (0)