@@ -187,20 +187,34 @@ void MakeConnected(DepGraph<BS>& depgraph)
187
187
188
188
/* * Given a dependency graph, and a todo set, read a topological subset of todo from reader. */
189
189
template <typename SetType>
190
- SetType ReadTopologicalSet (const DepGraph<SetType>& depgraph, const SetType& todo, SpanReader& reader)
190
+ SetType ReadTopologicalSet (const DepGraph<SetType>& depgraph, const SetType& todo, SpanReader& reader, bool non_empty )
191
191
{
192
+ // Read a bitmask from the fuzzing input. Add 1 if non_empty, so the mask is definitely not
193
+ // zero in that case.
192
194
uint64_t mask{0 };
193
195
try {
194
196
reader >> VARINT (mask);
195
197
} catch (const std::ios_base::failure&) {}
198
+ mask += non_empty;
199
+
196
200
SetType ret;
197
201
for (auto i : todo) {
198
202
if (!ret[i]) {
199
203
if (mask & 1 ) ret |= depgraph.Ancestors (i);
200
204
mask >>= 1 ;
201
205
}
202
206
}
203
- return ret & todo;
207
+ ret &= todo;
208
+
209
+ // While mask starts off non-zero if non_empty is true, it is still possible that all its low
210
+ // bits are 0, and ret ends up being empty. As a last resort, use the in-todo ancestry of the
211
+ // first todo position.
212
+ if (non_empty && ret.None ()) {
213
+ Assume (todo.Any ());
214
+ ret = depgraph.Ancestors (todo.First ()) & todo;
215
+ Assume (ret.Any ());
216
+ }
217
+ return ret;
204
218
}
205
219
206
220
/* * Given a dependency graph, construct any valid linearization for it, reading from a SpanReader. */
@@ -629,10 +643,10 @@ FUZZ_TARGET(clusterlin_ancestor_finder)
629
643
assert (real_best_anc.has_value ());
630
644
assert (*real_best_anc == best_anc);
631
645
632
- // Find a topologically valid subset of transactions to remove from the graph.
633
- auto del_set = ReadTopologicalSet (depgraph, todo, reader);
634
- // If we did not find anything, use best_anc itself, because we should remove something .
635
- if ( del_set. None ()) del_set = best_anc. transactions ;
646
+ // Find a non-empty topologically valid subset of transactions to remove from the graph.
647
+ // Using an empty set would mean the next iteration is identical to the current one, and
648
+ // could cause an infinite loop .
649
+ auto del_set = ReadTopologicalSet (depgraph, todo, reader, /* non_empty= */ true ) ;
636
650
todo -= del_set;
637
651
anc_finder.MarkDone (del_set);
638
652
}
@@ -708,15 +722,16 @@ FUZZ_TARGET(clusterlin_simple_finder)
708
722
assert (exhaustive.feerate == found.feerate );
709
723
}
710
724
711
- // Compare with a topological set read from the fuzz input.
712
- auto read_topo = ReadTopologicalSet (depgraph, todo, reader);
713
- if (read_topo.Any ()) assert (found.feerate >= depgraph.FeeRate (read_topo));
725
+ // Compare with a non-empty topological set read from the fuzz input (comparing with an
726
+ // empty set is not interesting).
727
+ auto read_topo = ReadTopologicalSet (depgraph, todo, reader, /* non_empty=*/ true );
728
+ assert (found.feerate >= depgraph.FeeRate (read_topo));
714
729
}
715
730
716
- // Find a topologically valid subset of transactions to remove from the graph.
717
- auto del_set = ReadTopologicalSet (depgraph, todo, reader);
718
- // If we did not find anything, use found itself, because we should remove something .
719
- if ( del_set. None ()) del_set = found. transactions ;
731
+ // Find a non-empty topologically valid subset of transactions to remove from the graph.
732
+ // Using an empty set would mean the next iteration is identical to the current one, and
733
+ // could cause an infinite loop .
734
+ auto del_set = ReadTopologicalSet (depgraph, todo, reader, /* non_empty= */ true ) ;
720
735
todo -= del_set;
721
736
smp_finder.MarkDone (del_set);
722
737
exh_finder.MarkDone (del_set);
@@ -766,8 +781,9 @@ FUZZ_TARGET(clusterlin_search_finder)
766
781
} catch (const std::ios_base::failure&) {}
767
782
max_iterations &= 0xfffff ;
768
783
769
- // Read an initial subset from the fuzz input.
770
- SetInfo init_best (depgraph, ReadTopologicalSet (depgraph, todo, reader));
784
+ // Read an initial subset from the fuzz input (allowed to be empty).
785
+ auto init_set = ReadTopologicalSet (depgraph, todo, reader, /* non_empty=*/ false );
786
+ SetInfo init_best (depgraph, init_set);
771
787
772
788
// Call the search finder's FindCandidateSet for what remains of the graph.
773
789
auto [found, iterations_done] = src_finder.FindCandidateSet (max_iterations, init_best);
@@ -812,15 +828,16 @@ FUZZ_TARGET(clusterlin_search_finder)
812
828
auto anc = anc_finder.FindCandidateSet ();
813
829
assert (found.feerate >= anc.feerate );
814
830
815
- // Compare with a topological set read from the fuzz input.
816
- auto read_topo = ReadTopologicalSet (depgraph, todo, reader);
817
- if (read_topo.Any ()) assert (found.feerate >= depgraph.FeeRate (read_topo));
831
+ // Compare with a non-empty topological set read from the fuzz input (comparing with an
832
+ // empty set is not interesting).
833
+ auto read_topo = ReadTopologicalSet (depgraph, todo, reader, /* non_empty=*/ true );
834
+ assert (found.feerate >= depgraph.FeeRate (read_topo));
818
835
}
819
836
820
- // Find a topologically valid subset of transactions to remove from the graph.
821
- auto del_set = ReadTopologicalSet (depgraph, todo, reader);
822
- // If we did not find anything, use found itself, because we should remove something .
823
- if ( del_set. None ()) del_set = found. transactions ;
837
+ // Find a non-empty topologically valid subset of transactions to remove from the graph.
838
+ // Using an empty set would mean the next iteration is identical to the current one, and
839
+ // could cause an infinite loop .
840
+ auto del_set = ReadTopologicalSet (depgraph, todo, reader, /* non_empty= */ true ) ;
824
841
todo -= del_set;
825
842
src_finder.MarkDone (del_set);
826
843
smp_finder.MarkDone (del_set);
@@ -844,9 +861,10 @@ FUZZ_TARGET(clusterlin_linearization_chunking)
844
861
reader >> Using<DepGraphFormatter>(depgraph);
845
862
} catch (const std::ios_base::failure&) {}
846
863
847
- // Retrieve a topologically-valid subset of depgraph.
864
+ // Retrieve a topologically-valid subset of depgraph (allowed to be empty, because the argument
865
+ // to LinearizationChunking::Intersect is allowed to be empty).
848
866
auto todo = depgraph.Positions ();
849
- auto subset = SetInfo (depgraph, ReadTopologicalSet (depgraph, todo, reader));
867
+ auto subset = SetInfo (depgraph, ReadTopologicalSet (depgraph, todo, reader, /* non_empty= */ false ));
850
868
851
869
// Retrieve a valid linearization for depgraph.
852
870
auto linearization = ReadLinearization (depgraph, reader);
@@ -935,13 +953,10 @@ FUZZ_TARGET(clusterlin_linearization_chunking)
935
953
}
936
954
}
937
955
938
- // Find a subset to remove from linearization.
939
- auto done = ReadTopologicalSet (depgraph, todo, reader);
940
- if (done.None ()) {
941
- // We need to remove a non-empty subset, so fall back to the unlinearized ancestors of
942
- // the first transaction in todo if done is empty.
943
- done = depgraph.Ancestors (todo.First ()) & todo;
944
- }
956
+ // Find a non-empty topologically valid subset of transactions to remove from the graph.
957
+ // Using an empty set would mean the next iteration is identical to the current one, and
958
+ // could cause an infinite loop.
959
+ auto done = ReadTopologicalSet (depgraph, todo, reader, /* non_empty=*/ true );
945
960
todo -= done;
946
961
chunking.MarkDone (done);
947
962
subset = SetInfo (depgraph, subset.transactions - done);
0 commit comments