summaryrefslogtreecommitdiff
path: root/pp_sort.c
diff options
context:
space:
mode:
authorZefram <zefram@fysh.org>2017-12-12 09:47:41 +0000
committerZefram <zefram@fysh.org>2017-12-12 09:53:32 +0000
commit16ada235c332e017667585e1a5a00ce43a31c529 (patch)
tree746383a2364185484906fefefa457d8879b7f84c /pp_sort.c
parent6cc23b5aabb7c0128c3c2bff71584f07f965efb4 (diff)
downloadperl-16ada235c332e017667585e1a5a00ce43a31c529.tar.gz
fix GvSV refcounting in sort
Where a sort operation passes the comparands to a comparison block in $a and $b, it wasn't taking account of the fact that the GvSV slots in *a and *b are refcounted. It would write the comparands into those slots without altering any reference counts, and end by restoring the values those slots had to start with. This was all fine so long as nothing else touched those slots during the process. But code running during the comparison is free to write to them by "*a = \1", which does frob the reference counts. Fix it by switching sort to manipulate GvSV in a refcount-preserving manner, compatible with all other operations on those slots. Fixes [perl #92264].
Diffstat (limited to 'pp_sort.c')
-rw-r--r--pp_sort.c15
1 files changed, 11 insertions, 4 deletions
diff --git a/pp_sort.c b/pp_sort.c
index fb4e2f8b8e..8be778e2f2 100644
--- a/pp_sort.c
+++ b/pp_sort.c
@@ -981,8 +981,10 @@ PP(pp_sort)
/* we don't want modifications localized */
GvINTRO_off(PL_firstgv);
GvINTRO_off(PL_secondgv);
- SAVESPTR(GvSV(PL_firstgv));
- SAVESPTR(GvSV(PL_secondgv));
+ SAVEGENERICSV(GvSV(PL_firstgv));
+ SvREFCNT_inc(GvSV(PL_firstgv));
+ SAVEGENERICSV(GvSV(PL_secondgv));
+ SvREFCNT_inc(GvSV(PL_secondgv));
}
gimme = G_SCALAR;
@@ -1118,11 +1120,16 @@ S_sortcv(pTHX_ SV *const a, SV *const b)
I32 result;
PMOP * const pm = PL_curpm;
COP * const cop = PL_curcop;
+ SV *olda, *oldb;
PERL_ARGS_ASSERT_SORTCV;
- GvSV(PL_firstgv) = a;
- GvSV(PL_secondgv) = b;
+ olda = GvSV(PL_firstgv);
+ GvSV(PL_firstgv) = SvREFCNT_inc_simple_NN(a);
+ SvREFCNT_dec(olda);
+ oldb = GvSV(PL_secondgv);
+ GvSV(PL_secondgv) = SvREFCNT_inc_simple_NN(b);
+ SvREFCNT_dec(oldb);
PL_stack_sp = PL_stack_base;
PL_op = PL_sortcop;
CALLRUNOPS(aTHX);