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
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
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
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
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
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
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
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)); +