From d7c023eb25edacff98144e0dcf394af7c79d70bb Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 10 Mar 2023 16:07:32 -0300 Subject: [PATCH] Fix #105583: crash when weld modifier checks for duplicate polygons In very specific cases, during intersection testing, `intersect` can add polygons already checked as duplicates in the buffer that corresponds to the rest of polygons that can form groups of duplicates. As the buffer cannot have repeated indices, re-adding, even temporarily, these duplicates can cause a buffer overflow. While this may have some impact on performance, it's difficult to predict these cases and thus add a buffer pad. So the solution is to check if they are already duplicated. --- .../geometry/intern/mesh_merge_by_distance.cc | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/source/blender/geometry/intern/mesh_merge_by_distance.cc b/source/blender/geometry/intern/mesh_merge_by_distance.cc index b9295a8344d..92f6476fdd9 100644 --- a/source/blender/geometry/intern/mesh_merge_by_distance.cc +++ b/source/blender/geometry/intern/mesh_merge_by_distance.cc @@ -1102,8 +1102,11 @@ static int poly_find_doubles(const OffsetIndices poly_corners_offsets, { /* Fills the `r_buffer` buffer with the intersection of the arrays in `buffer_a` and `buffer_b`. * `buffer_a` and `buffer_b` have a sequence of sorted, non-repeating indices representing - * polygons. */ - const auto intersect = [](const Span buffer_a, const Span buffer_b, int *r_buffer) { + * polygons. */ + const auto intersect = [](const Span buffer_a, + const Span buffer_b, + const BitVector<> is_double, + int *r_buffer) { int result_num = 0; int index_a = 0, index_b = 0; while (index_a < buffer_a.size() && index_b < buffer_b.size()) { @@ -1117,7 +1120,12 @@ static int poly_find_doubles(const OffsetIndices poly_corners_offsets, } else { /* Equality. */ - r_buffer[result_num++] = value_a; + + /* Do not add duplicates. + * As they are already in the original array, this can cause buffer overflow. */ + if (!is_double[value_a]) { + r_buffer[result_num++] = value_a; + } index_a++; index_b++; } @@ -1204,6 +1212,7 @@ static int poly_find_doubles(const OffsetIndices poly_corners_offsets, int *isect_result = doubles_buffer.data() + doubles_buffer_num + 1; + /* `polys_a` are the polygons connected to the first corner. So skip the first corner. */ for (int corner_index : IndexRange(corner_first + 1, corner_num - 1)) { elem_index = corners[corner_index]; link_offs = linked_polys_offset[elem_index]; @@ -1217,8 +1226,10 @@ static int poly_find_doubles(const OffsetIndices poly_corners_offsets, polys_b_num--; } while (poly_to_test != poly_index); - doubles_num = intersect( - Span{polys_a, polys_a_num}, Span{polys_b, polys_b_num}, isect_result); + doubles_num = intersect(Span{polys_a, polys_a_num}, + Span{polys_b, polys_b_num}, + is_double, + isect_result); if (doubles_num == 0) { break; @@ -1236,6 +1247,12 @@ static int poly_find_doubles(const OffsetIndices poly_corners_offsets, } doubles_buffer_num += doubles_num; doubles_offsets.append(++doubles_buffer_num); + + if ((doubles_buffer_num + 1) == poly_num) { + /* The last slot is the remaining unduplicated polygon. + * Avoid checking intersection as there are no more slots left. */ + break; + } } }