Commit 62075d12 for tesseract

commit 62075d12840673913e22fb1f86d0b86aad6eae89
Author: Stefan Weil <sw@weilnetz.de>
Date:   Sun Aug 9 15:03:31 2026 +0200

    Fix CID 1438673 (Use after free)

    The TableFinder takes ownership of a partition passed to
    InsertTextPartition and may delete it if it is not accepted.
    The test helper tracked the partition in a cleanup list
    regardless, and the tests dereferenced the partition after
    the insertion, both of which could use a freed partition.
    Track the partition only if the finder keeps it, and copy
    the blob references before inserting the partition.

    Assisted-by: OpenCode / big-pickle (opencode)
    Signed-off-by: Stefan Weil <sw@weilnetz.de>

diff --git a/unittest/tablefind_test.cc b/unittest/tablefind_test.cc
index 25b6e626..245e9a8d 100644
--- a/unittest/tablefind_test.cc
+++ b/unittest/tablefind_test.cc
@@ -29,6 +29,7 @@ public:
   using TableFinder::set_global_median_ledding;
   using TableFinder::set_global_median_xheight;
   using TableFinder::SplitAndInsertFragmentedTextPartition;
+  using TableFinder::AllowTextPartition;

   void ExpectPartition(const TBOX &box) {
     tesseract::ColPartitionGridSearch gsearch(&fragmented_text_grid_);
@@ -95,8 +96,13 @@ protected:
   }

   void InsertTextPartition(ColPartition *part) {
+    // The finder takes ownership of the partition and either inserts it
+    // into a grid or deletes it. Only keep track of it for the cleanup
+    // in TearDown if the finder keeps it.
+    if (finder_->AllowTextPartition(*part)) {
+      free_boxes_it_.add_after_then_move(part);
+    }
     finder_->InsertTextPartition(part);
-    free_boxes_it_.add_after_then_move(part);
   }

   void InsertLeaderPartition(int x_min, int y_min, int x_max, int y_max) {
@@ -235,9 +241,8 @@ TEST_F(TableFinderTest, SplitAndInsertFragmentedPartitionsBasicPass) {
   // TODO(nbeato): Ray's newer code...
   // all->ClaimBoxes();
   all->ComputeLimits();     // This is to make sure median iinfo is set.
-  InsertTextPartition(all); // This is to delete blobs
   ColPartition *fragment_me = all->CopyButDontOwnBlobs();
-
+  InsertTextPartition(all); // This is to delete blobs
   finder_->SplitAndInsertFragmentedTextPartition(fragment_me);
   finder_->ExpectPartition(TBOX(11, 5, 24, 15));
   finder_->ExpectPartition(TBOX(36, 5, 59, 15));
@@ -265,9 +270,8 @@ TEST_F(TableFinderTest, SplitAndInsertFragmentedPartitionsBasicFail) {
   // TODO(nbeato): Ray's newer code...
   // all->ClaimBoxes();
   all->ComputeLimits();     // This is to make sure median iinfo is set.
-  InsertTextPartition(all); // This is to delete blobs
   ColPartition *fragment_me = all->CopyButDontOwnBlobs();
-
+  InsertTextPartition(all); // This is to delete blobs
   finder_->SplitAndInsertFragmentedTextPartition(fragment_me);
   finder_->ExpectPartition(TBOX(11, 5, 99, 15));
   finder_->ExpectPartitionCount(1);