Change save/restore behavior for comparisons
authorjpl <jpl.jpl@gmail.com>
Mon, 28 Aug 2017 13:54:15 +0000 (09:54 -0400)
committerTony Cook <tony@develop-help.com>
Thu, 21 Sep 2017 01:00:41 +0000 (11:00 +1000)
S_mergesortsv was saving the current comparison routine only when the
SORTf_DESC flag was set, but "restoring" it when ANY flag was set.
When some flag other than SORTf_DESC was set, this could lead to
the pointer to the comparison routine being set to NULL,
triggering a segfault when the routine was subsequently invoked.

lib/sort.t
pp_sort.c

index b44269a..1ff3832 100644 (file)
@@ -130,9 +130,24 @@ sub main {
     }
 }
 
-# Test with no pragma still loaded -- stability expected (this is a mergesort)
+# Test with no pragma yet loaded. Stability is expected from default sort.
 main(sub { sort {&{$_[0]}} @{$_[1]} }, 0);
 
+# Verify that we have eliminated the segfault that could be triggered
+# by invoking a sort as part of a comparison routine.
+# No need for an explicit test. If we don't segfault, we're good.
+
+{
+    sub dumbsort {
+       my ($a, $b) = @_;
+       use sort qw( defaults stable );
+       my @ignore = sort (5,4,3,2,1);
+       return $a <=> $b;
+    }
+    use sort qw( defaults _qsort stable );
+    my @nested = sort { dumbsort($a,$b) } (3,2,2,1);
+}
+
 {
     use sort qw(_qsort);
     my $sort_current; BEGIN { $sort_current = sort::current(); }
index 6049509..9d31bda 100644 (file)
--- a/pp_sort.c
+++ b/pp_sort.c
@@ -558,7 +558,7 @@ S_mergesortsv(pTHX_ gptr *base, size_t nmemb, SVCOMPARE_t cmp, U32 flags)
     }
   done:
     if (aux != small) Safefree(aux);   /* free iff allocated */
-    if (flags) {
+    if (savecmp != NULL) {
         PL_sort_RealCmp = savecmp;     /* Restore current comparison routine, if any */
     }
     return;