Re: svn commit: r1872118 - in /subversion/trunk/subversion: include/private/svn_sorts_private.h libsvn_client/merge.c libsvn_subr/mergeinfo.c libsvn_subr/sorts.c tests/libsvn_subr/mergeinfo-test.c

2019-12-30 Thread Daniel Shahaf
julianf...@apache.org wrote on Mon, Dec 30, 2019 at 15:42:17 -:
> * subversion/libsvn_subr/mergeinfo.c
>   (svn_rangelist_merge2): Extract the body of this function into a local
> 'rangelist_merge2', leaving the original as an error-checking
> wrapper. If an error occurs, report its inputs.
> 
> +++ subversion/trunk/subversion/libsvn_subr/mergeinfo.c Mon Dec 30 15:42:17 
> 2019
> @@ -1265,40 +1278,98 @@ svn_rangelist_merge2(svn_rangelist_t *ra
>  }
>  
>  #ifdef SVN_DEBUG
> -  SVN_ERR_ASSERT(svn_rangelist__is_canonical(rangelist));
> +  /*SVN_ERR_ASSERT(svn_rangelist__is_canonical(rangelist));*/

Was this change intended to be committed?

>  #endif
>  
>return SVN_NO_ERROR;
>  }



svn commit: r1872121 - /subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c

2019-12-30 Thread julianfoad
Author: julianfoad
Date: Mon Dec 30 16:34:00 2019
New Revision: 1872121

URL: http://svn.apache.org/viewvc?rev=1872121=rev
Log:
Random-input testing for svn_mergeinfo_merge2().

For issue #4840, "Merge assertion failure in svn_sort__array_insert".

* subversion/tests/libsvn_subr/mergeinfo-test.c
  (mergeinfo_random_non_validated,
   mergeinfo_to_string_debug,
   mergeinfo_merge_random_inputs): New.
  (test_mergeinfo_merge_random_non_validated_inputs): New test.
  (test_funcs): Run it.

Modified:
subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c

Modified: subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c?rev=1872121=1872120=1872121=diff
==
--- subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c Mon Dec 30 
16:34:00 2019
@@ -2154,6 +2154,114 @@ test_rangelist_merge_random_non_validate
 
   return SVN_NO_ERROR;
 }
+
+/* Generate random mergeinfo, in which the paths and rangelists are not
+ * necessarily valid. */
+static svn_error_t *
+mergeinfo_random_non_validated(svn_mergeinfo_t *mp,
+   apr_pool_t *pool)
+{
+  svn_mergeinfo_t m = apr_hash_make(pool);
+  int i;
+
+  for (i = 0; i < 3; i++)
+{
+  const char *path;
+  svn_rangelist_t *rl;
+
+  switch (rand_less_than(8))
+{
+case 0: case 1: case 2: case 3:
+  path = apr_psprintf(pool, "/path%d", i); break;
+case 4:
+  path = apr_psprintf(pool, "path%d", i); break;
+case 5:
+  path = apr_psprintf(pool, "//path%d", i); break;
+case 6:
+  path = "/"; break;
+case 7:
+  path = ""; break;
+}
+  rangelist_random_non_validated(, pool);
+  svn_hash_sets(m, path, rl);
+}
+  *mp = m;
+  return SVN_NO_ERROR;
+}
+
+#if 0
+static const char *
+mergeinfo_to_string_debug(svn_mergeinfo_t m,
+  apr_pool_t *pool)
+{
+  svn_string_t *s;
+  svn_error_t *err;
+
+  err = svn_mergeinfo_to_string(, m, pool);
+  if (err)
+{
+  const char *s2 = err->message;
+  svn_error_clear(err);
+  return s2;
+}
+  return s->data;
+}
+#endif
+
+/* Try a mergeinfo merge.  This does not check the result. */
+static svn_error_t *
+mergeinfo_merge_random_inputs(const svn_mergeinfo_t mx,
+  const svn_mergeinfo_t my,
+  apr_pool_t *pool)
+{
+  svn_mergeinfo_t mm = svn_mergeinfo_dup(mx, pool);
+
+  SVN_ERR(svn_mergeinfo_merge2(mm, my, pool, pool));
+  return SVN_NO_ERROR;
+}
+
+/* Test svn_mergeinfo_merge2() with random non-validated inputs.
+ *
+ * Unlike the tests with valid inputs, this test expects many assertion
+ * failures.  We don't care about those.  All we care about is that it does
+ * not crash. */
+static svn_error_t *
+test_mergeinfo_merge_random_non_validated_inputs(apr_pool_t *pool)
+{
+  apr_pool_t *iterpool = svn_pool_create(pool);
+  int ix, iy;
+
+  for (ix = 0; ix < 300; ix++)
+   {
+svn_mergeinfo_t mx;
+
+SVN_ERR(mergeinfo_random_non_validated(, pool));
+
+for (iy = 0; iy < 300; iy++)
+  {
+svn_mergeinfo_t my;
+svn_error_t *err;
+
+svn_pool_clear(iterpool);
+
+SVN_ERR(mergeinfo_random_non_validated(, iterpool));
+
+err = mergeinfo_merge_random_inputs(mx, my, iterpool);
+if (err)
+  {
+/*
+printf("testcase FAIL: %s / %s\n",
+   mergeinfo_to_string_debug(mx, iterpool),
+   mergeinfo_to_string_debug(my, iterpool));
+svn_handle_error(err, stdout, FALSE);
+*/
+svn_error_clear(err);
+  }
+  }
+   }
+
+  return SVN_NO_ERROR;
+}
 
 /* The test table.  */
 
@@ -2210,6 +2318,8 @@ static struct svn_test_descriptor_t test
 "test rangelist merge random semi-c inputs"),
 SVN_TEST_PASS2(test_rangelist_merge_random_non_validated_inputs,
"test rangelist merge random non-validated inputs"),
+SVN_TEST_PASS2(test_mergeinfo_merge_random_non_validated_inputs,
+   "test mergeinfo merge random non-validated inputs"),
 SVN_TEST_NULL
   };
 




svn commit: r1872118 - in /subversion/trunk/subversion: include/private/svn_sorts_private.h libsvn_client/merge.c libsvn_subr/mergeinfo.c libsvn_subr/sorts.c tests/libsvn_subr/mergeinfo-test.c

2019-12-30 Thread julianfoad
Author: julianfoad
Date: Mon Dec 30 15:42:17 2019
New Revision: 1872118

URL: http://svn.apache.org/viewvc?rev=1872118=rev
Log:
Avoid aborting on assertion failure in the area of mergeinfo calculations.

Instead, raise a catchable assertion error.

If an error occurs in svn_rangelist_merge2(), produce a more detailed error
message to aid in debugging.

Introduce and use 'svn_sort__array_insert2()' which checks its inputs.  The
old version of this function was aborting on out-of-range inputs.  Introduce
and use 'svn_sort__array_delete2()' likewise, as similar issues may show up
here too.  The old version of this function was ignoring calls with
out-of-range inputs.  The old versions of both functions are still in use
elsewhere in the Subversion libraries.

For issue #4840, "Merge assertion failure in svn_sort__array_insert".

* subversion/include/private/svn_sorts_private.h,
  subversion/libsvn_subr/sorts.c
  (svn_sort__array_insert2,
   svn_sort__array_delete2): New.

* subversion/libsvn_client/merge.c
  (slice_remaining_ranges,
   insert_child_to_merge): Allow returning an error.
  Everywhere: use svn_sort__array_insert2() and svn_sort__array_delete2().

* subversion/libsvn_subr/mergeinfo.c
  (adjust_remaining_ranges): Allow returning an error.
  (dual_dump): Replace this old debug helper...
  (rangelist_to_string_debug): ... with this new one.
  (svn_rangelist_merge2): Extract the body of this function into a local
'rangelist_merge2', leaving the original as an error-checking
wrapper. If an error occurs, report its inputs.
  (rangelist_is_sorted): New.
  Everywhere: use svn_sort__array_insert2() and svn_sort__array_delete2().

* subversion/tests/libsvn_subr/mergeinfo-test.c
  (test_rangelist_merge_random_non_validated_inputs): Expect assertion
failures; ignore them.
  (test_funcs): Expect that test to pass now.

Modified:
subversion/trunk/subversion/include/private/svn_sorts_private.h
subversion/trunk/subversion/libsvn_client/merge.c
subversion/trunk/subversion/libsvn_subr/mergeinfo.c
subversion/trunk/subversion/libsvn_subr/sorts.c
subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c

Modified: subversion/trunk/subversion/include/private/svn_sorts_private.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_sorts_private.h?rev=1872118=1872117=1872118=diff
==
--- subversion/trunk/subversion/include/private/svn_sorts_private.h (original)
+++ subversion/trunk/subversion/include/private/svn_sorts_private.h Mon Dec 30 
15:42:17 2019
@@ -127,6 +127,16 @@ svn_sort__array_insert(apr_array_header_
const void *new_element,
int insert_index);
 
+/* Like svn_sort__array_insert() but raise an error if @a insert_index
+ * is less than 0 or greater than the length of the array.
+ *
+ * @note Private. For use by Subversion's own code only.
+ */
+svn_error_t *
+svn_sort__array_insert2(apr_array_header_t *array,
+const void *new_element,
+int insert_index);
+
 
 /* Remove @a elements_to_delete elements starting at @a delete_index from the
  * array @a arr. If @a delete_index is not a valid element of @a arr,
@@ -141,6 +151,16 @@ svn_sort__array_delete(apr_array_header_
int delete_index,
int elements_to_delete);
 
+/* Like svn_sort__array_delete() but raise an error if attempting
+ * to delete a range of elements that goes out of bounds of the array.
+ *
+ * @note Private. For use by Subversion's own code only.
+ */
+svn_error_t *
+svn_sort__array_delete2(apr_array_header_t *arr,
+int delete_index,
+int elements_to_delete);
+
 /* Reverse the order of elements in @a array, in place.
  *
  * @note Private. For use by Subversion's own code only.

Modified: subversion/trunk/subversion/libsvn_client/merge.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/merge.c?rev=1872118=1872117=1872118=diff
==
--- subversion/trunk/subversion/libsvn_client/merge.c (original)
+++ subversion/trunk/subversion/libsvn_client/merge.c Mon Dec 30 15:42:17 2019
@@ -5975,7 +5975,7 @@ get_most_inclusive_rev(const apr_array_h
remaining_ranges is inclusive of END_REV, Slice the first range in
to two at END_REV. All the allocations are persistent and allocated
from POOL. */
-static void
+static svn_error_t *
 slice_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
svn_boolean_t is_rollback, svn_revnum_t end_rev,
apr_pool_t *pool)
@@ -6005,10 +6005,12 @@ slice_remaining_ranges(apr_array_header_
   split_range2->start = end_rev;
   APR_ARRAY_IDX(child->remaining_ranges, 0,
 svn_merge_range_t *) = 

svn commit: r1872115 - /subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c

2019-12-30 Thread julianfoad
Author: julianfoad
Date: Mon Dec 30 14:40:56 2019
New Revision: 1872115

URL: http://svn.apache.org/viewvc?rev=1872115=rev
Log:
Use a matching integer type instead of casts, as a cleaner follow-up to 
r1872108.

Suggested by: brane

Modified:
subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c

Modified: subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c?rev=1872115=1872114=1872115=diff
==
--- subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c Mon Dec 30 
14:40:56 2019
@@ -1839,9 +1839,9 @@ rangelist_to_array(rl_array_t *a,
   for (i = 0; i < rl->nelts; i++)
 {
   svn_merge_range_t *range = APR_ARRAY_IDX(rl, i, svn_merge_range_t *);
-  int r;
+  svn_revnum_t r;
 
-  for (r = (int)range->start + 1; r <= (int)range->end; r++)
+  for (r = range->start + 1; r <= range->end; r++)
 {
   a->root[r] = TRUE;
   a->inherit[r] = range->inheritable;
@@ -1857,7 +1857,7 @@ rangelist_array_union(rl_array_t *ma,
   const rl_array_t *ba,
   const rl_array_t *ca)
 {
-  int r;
+  svn_revnum_t r;
 
   for (r = 0; r <= RANGELIST_TESTS_MAX_REV; r++)
 {
@@ -1872,7 +1872,7 @@ static svn_boolean_t
 rangelist_array_equal(const rl_array_t *ba,
   const rl_array_t *ca)
 {
-  int r;
+  svn_revnum_t r;
 
   for (r = 0; r <= RANGELIST_TESTS_MAX_REV; r++)
 {




svn commit: r1872108 - /subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c

2019-12-30 Thread julianfoad
Author: julianfoad
Date: Mon Dec 30 12:24:50 2019
New Revision: 1872108

URL: http://svn.apache.org/viewvc?rev=1872108=rev
Log:
Avoid integer conversion warning. A follow-up to r1872107.

* subversion/tests/libsvn_subr/mergeinfo-test.c
  (rangelist_to_array): Add explicit casts.

Modified:
subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c

Modified: subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c?rev=1872108=1872107=1872108=diff
==
--- subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c Mon Dec 30 
12:24:50 2019
@@ -1841,7 +1841,7 @@ rangelist_to_array(rl_array_t *a,
   svn_merge_range_t *range = APR_ARRAY_IDX(rl, i, svn_merge_range_t *);
   int r;
 
-  for (r = range->start + 1; r <= range->end; r++)
+  for (r = (int)range->start + 1; r <= (int)range->end; r++)
 {
   a->root[r] = TRUE;
   a->inherit[r] = range->inheritable;




svn commit: r1872107 - /subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c

2019-12-30 Thread julianfoad
Author: julianfoad
Date: Mon Dec 30 11:53:55 2019
New Revision: 1872107

URL: http://svn.apache.org/viewvc?rev=1872107=rev
Log:
Random-input testing for issue #4840, "Merge assertion failure in
svn_sort__array_insert".

This adds tests for svn_rangelist_merge2() with canonical inputs,
with "semi-canonical" inputs which meet criteria described in its
doc string, and with non-validated inputs.

* subversion/tests/libsvn_subr/mergeinfo-test.c
  (...): Helper functions.
  (test_rangelist_merge_random_canonical_inputs,
   test_rangelist_merge_random_semi_c_inputs,
   test_rangelist_merge_random_non_validated_inputs): New tests.
  (test_funcs): Run them.

Modified:
subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c

Modified: subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c?rev=1872107=1872106=1872107=diff
==
--- subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_subr/mergeinfo-test.c Mon Dec 30 
11:53:55 2019
@@ -33,6 +33,7 @@
 #include "svn_types.h"
 #include "svn_mergeinfo.h"
 #include "private/svn_mergeinfo_private.h"
+#include "private/svn_sorts_private.h"
 #include "../svn_test.h"
 
 /* A quick way to create error messages.  */
@@ -1811,6 +1812,351 @@ test_rangelist_merge_canonical_result(ap
 
   return SVN_NO_ERROR;
 }
+
+
+/* Randomize revision numbers over this small range.
+ * (With a larger range, we would find edge cases more rarely.) */
+#define RANGELIST_TESTS_MAX_REV 15
+
+/* A representation of svn_rangelist_t in which
+ *   root[R]:= (revision R is in the rangelist)
+ *   inherit[R] := (revision R is in the rangelist and inheritable)
+ *
+ * Assuming all forward ranges.
+ */
+typedef struct rl_array_t {
+svn_boolean_t root[RANGELIST_TESTS_MAX_REV + 1];
+svn_boolean_t inherit[RANGELIST_TESTS_MAX_REV + 1];
+} rl_array_t;
+
+static void
+rangelist_to_array(rl_array_t *a,
+   const svn_rangelist_t *rl)
+{
+  int i;
+
+  memset(a, 0, sizeof(*a));
+  for (i = 0; i < rl->nelts; i++)
+{
+  svn_merge_range_t *range = APR_ARRAY_IDX(rl, i, svn_merge_range_t *);
+  int r;
+
+  for (r = range->start + 1; r <= range->end; r++)
+{
+  a->root[r] = TRUE;
+  a->inherit[r] = range->inheritable;
+}
+}
+}
+
+/* Compute the union of two rangelists arrays.
+ * Let MA := union(BA, CA)
+ */
+static void
+rangelist_array_union(rl_array_t *ma,
+  const rl_array_t *ba,
+  const rl_array_t *ca)
+{
+  int r;
+
+  for (r = 0; r <= RANGELIST_TESTS_MAX_REV; r++)
+{
+  ma->root[r]= ba->root[r]|| ca->root[r];
+  ma->inherit[r] = ba->inherit[r] || ca->inherit[r];
+}
+}
+
+/* Return TRUE iff two rangelist arrays are equal.
+ */
+static svn_boolean_t
+rangelist_array_equal(const rl_array_t *ba,
+  const rl_array_t *ca)
+{
+  int r;
+
+  for (r = 0; r <= RANGELIST_TESTS_MAX_REV; r++)
+{
+  if (ba->root[r]!= ca->root[r]
+   || ba->inherit[r] != ca->inherit[r])
+{
+  return FALSE;
+}
+}
+  return TRUE;
+}
+
+#define IS_VALID_FORWARD_RANGE(range) \
+  (SVN_IS_VALID_REVNUM((range)->start) && ((range)->start < (range)->end))
+
+/* Check rangelist is sorted and contains forward ranges. */
+static svn_boolean_t
+rangelist_is_sorted(const svn_rangelist_t *rangelist)
+{
+  int i;
+
+  for (i = 0; i < rangelist->nelts; i++)
+{
+  const svn_merge_range_t *thisrange
+= APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
+
+  if (!IS_VALID_FORWARD_RANGE(thisrange))
+return FALSE;
+}
+  for (i = 1; i < rangelist->nelts; i++)
+{
+  const svn_merge_range_t *lastrange
+= APR_ARRAY_IDX(rangelist, i-1, svn_merge_range_t *);
+  const svn_merge_range_t *thisrange
+= APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
+
+  if (svn_sort_compare_ranges(, ) > 0)
+return FALSE;
+}
+  return TRUE;
+}
+
+/* Return a random number R, where 0 <= R < N.
+ */
+static int rand_less_than(int n)
+{
+  apr_uint32_t next = svn_test_rand(_rev_array_seed);
+  return ((apr_uint64_t)next * n) >> 32;
+}
+
+/* Generate a rangelist with a random number of random ranges.
+ */
+static void
+rangelist_random_non_validated(svn_rangelist_t **rl,
+   apr_pool_t *pool)
+{
+  svn_rangelist_t *r = apr_array_make(pool, 4, sizeof(svn_merge_range_t *));
+  int i;
+
+  /* Choose from 0 to 4 ranges, biased towards 2 ranges */
+  for (i = rand_less_than(3) + rand_less_than(3); i > 0; --i)
+{
+  svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange));
+
+  mrange->start = rand_less_than(RANGELIST_TESTS_MAX_REV + 1);
+  mrange->end = rand_less_than(RANGELIST_TESTS_MAX_REV + 1);
+  

svn commit: r1872104 - /subversion/trunk/subversion/libsvn_subr/mergeinfo.c

2019-12-30 Thread julianfoad
Author: julianfoad
Date: Mon Dec 30 10:44:43 2019
New Revision: 1872104

URL: http://svn.apache.org/viewvc?rev=1872104=rev
Log:
Raise an error on trying to convert invalid mergeinfo to a string.

Instead of converting invalid mergeinfo to a diagnostic string (r1872030),
immediately raise an error so as not to risk the diagnostic string finding
its way into versioned data.

A follow-up to r1872030.

For issue #4840, "Merge assertion failure in svn_sort__array_insert".

Suggested by: danielsh

* subversion/libsvn_subr/mergeinfo.c
  (range_to_string): Raise an error on invalid input.
  (range_to_string_debug): New.
  (svn_rangelist__canonicalize): Use range_to_string_debug() within an error
message.
  (svn_rangelist_to_string): Update the calls to range_to_string().

Modified:
subversion/trunk/subversion/libsvn_subr/mergeinfo.c

Modified: subversion/trunk/subversion/libsvn_subr/mergeinfo.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/mergeinfo.c?rev=1872104=1872103=1872104=diff
==
--- subversion/trunk/subversion/libsvn_subr/mergeinfo.c (original)
+++ subversion/trunk/subversion/libsvn_subr/mergeinfo.c Mon Dec 30 10:44:43 2019
@@ -457,23 +457,48 @@ combine_with_lastrange(const svn_merge_r
 }
 
 /* Convert a single svn_merge_range_t *RANGE back into a string.  */
-static char *
-range_to_string(const svn_merge_range_t *range,
+static svn_error_t *
+range_to_string(char **s,
+const svn_merge_range_t *range,
 apr_pool_t *pool)
 {
   const char *mark
 = range->inheritable ? "" : SVN_MERGEINFO_NONINHERITABLE_STR;
 
   if (range->start == range->end - 1)
-return apr_psprintf(pool, "%ld%s", range->end, mark);
+*s = apr_psprintf(pool, "%ld%s", range->end, mark);
   else if (range->start - 1 == range->end)
-return apr_psprintf(pool, "-%ld%s", range->start, mark);
+*s = apr_psprintf(pool, "-%ld%s", range->start, mark);
   else if (range->start < range->end)
-return apr_psprintf(pool, "%ld-%ld%s", range->start + 1, range->end, mark);
+*s = apr_psprintf(pool, "%ld-%ld%s", range->start + 1, range->end, mark);
   else if (range->start > range->end)
-return apr_psprintf(pool, "%ld-%ld%s", range->start, range->end + 1, mark);
+*s = apr_psprintf(pool, "%ld-%ld%s", range->start, range->end + 1, mark);
   else
-return apr_psprintf(pool, "[empty-range@%ld%s]", range->start, mark);
+{
+  return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
+   _("bad range 
{start=%ld,end=%ld,inheritable=%d}"),
+   range->start, range->end, range->inheritable);
+}
+
+  return SVN_NO_ERROR;
+}
+
+/* Convert a single svn_merge_range_t *RANGE back into a string.  */
+static char *
+range_to_string_debug(const svn_merge_range_t *range,
+  apr_pool_t *pool)
+{
+  svn_error_t *err;
+  char *s;
+
+  err = range_to_string(, range, pool);
+  if (err)
+{
+  svn_error_clear(err);
+  s = apr_psprintf(pool, _("bad range {start=%ld,end=%ld,inheritable=%d}"),
+   range->start, range->end, range->inheritable);
+}
+  return s;
 }
 
 /* Helper for svn_mergeinfo_parse()
@@ -669,10 +694,10 @@ svn_rangelist__canonicalize(svn_rangelis
  "revision ranges '%s' and '%s' "
  "with different inheritance "
  "types"),
-   range_to_string(lastrange,
-   scratch_pool),
-   range_to_string(range,
-   scratch_pool));
+   range_to_string_debug(lastrange,
+ scratch_pool),
+   range_to_string_debug(range,
+ scratch_pool));
 }
 
   /* Combine overlapping or adjacent ranges with the
@@ -1979,18 +2004,21 @@ svn_rangelist_to_string(svn_string_t **o
 {
   int i;
   svn_merge_range_t *range;
+  char *s;
 
   /* Handle the elements that need commas at the end.  */
   for (i = 0; i < rangelist->nelts - 1; i++)
 {
   range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
-  svn_stringbuf_appendcstr(buf, range_to_string(range, pool));
+  SVN_ERR(range_to_string(, range, pool));
+  svn_stringbuf_appendcstr(buf, s);
   svn_stringbuf_appendcstr(buf, ",");
 }
 
   /* Now handle the last element, which needs no comma.  */
   range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
-  svn_stringbuf_appendcstr(buf, range_to_string(range, pool));
+  SVN_ERR(range_to_string(, range, pool));
+