Commit 0d4e1a54 for tesseract

commit 0d4e1a54d812c12eb39bf9d3fd886dff2e613bd0
Author: Stefan Weil <sw@weilnetz.de>
Date:   Fri Nov 13 17:30:50 2026 +0100

    cutil: fix null pointer dereference for the size of an empty LIST

    An empty LIST is NIL_LIST (nullptr). Commit 2fa96b765 replaced the
    function count(LIST) by the member function list_rec::size(). Calling
    it for an empty list is undefined behaviour: clang removes the test
    for a null pointer, and the code crashes. That happened in
    PrintAdaptedTemplates() and in WriteAdaptedClass() for an adapted
    class without temporary protos, for example at the end of a run with
    classify_save_adapted_templates.

    Restore the function count(), which handles an empty list.

    Fix proposed by the Anthropic OSS Scanner (finding ANT-2026-8AGHSUUG).

    Assisted-by: OpenCode / Claude Fable 5.2 (Anthropic)
    Signed-off-by: Stefan Weil <sw@weilnetz.de>

diff --git a/src/classify/adaptive.cpp b/src/classify/adaptive.cpp
index c371acad..ca47d703 100644
--- a/src/classify/adaptive.cpp
+++ b/src/classify/adaptive.cpp
@@ -183,7 +183,7 @@ void Classify::PrintAdaptedTemplates(FILE *File, ADAPT_TEMPLATES_STRUCT *Templat
     if (!IsEmptyAdaptedClass(AClass)) {
       fprintf(File, "%5u  %s %3d %3d %3d %3zd\n", i, unicharset.id_to_unichar(i), IClass->NumConfigs,
               AClass->NumPermConfigs, IClass->NumProtos,
-              IClass->NumProtos - AClass->TempProtos->size());
+              IClass->NumProtos - count(AClass->TempProtos));
     }
   }
   fprintf(File, "\n");
@@ -387,7 +387,7 @@ void WriteAdaptedClass(FILE *File, ADAPT_CLASS_STRUCT *Class, int NumConfigs) {
   fwrite(Class->PermConfigs, sizeof(uint32_t), WordsInVectorOfSize(MAX_NUM_CONFIGS), File);

   /* then write out the list of temporary protos */
-  uint32_t NumTempProtos = Class->TempProtos->size();
+  uint32_t NumTempProtos = count(Class->TempProtos);
   fwrite(&NumTempProtos, sizeof(NumTempProtos), 1, File);
   auto TempProtos = Class->TempProtos;
   iterate(TempProtos) {
diff --git a/src/cutil/oldlist.cpp b/src/cutil/oldlist.cpp
index 583dd4b3..12869f8e 100644
--- a/src/cutil/oldlist.cpp
+++ b/src/cutil/oldlist.cpp
@@ -77,6 +77,17 @@ static int is_same(void *item1, void *item2) {
   return strcmp(static_cast<char *>(item1), static_cast<char *>(item2)) == 0;
 }

+/**********************************************************************
+ *  c o u n t
+ *
+ *  Count the elements in a list (0 for NIL_LIST).  Return the count.
+ **********************************************************************/
+size_t count(LIST var_list) {
+  size_t n = 0;
+  iterate(var_list) n++;
+  return n;
+}
+
 /**********************************************************************
  *  d e l e t e    d
  *
diff --git a/src/cutil/oldlist.h b/src/cutil/oldlist.h
index 41ef1d05..f7888a7d 100644
--- a/src/cutil/oldlist.h
+++ b/src/cutil/oldlist.h
@@ -111,16 +111,6 @@ struct list_rec {
   list_rec *list_rest() {
     return next;
   }
-
-  //********************************************************************
-  // Recursively count the elements in  a list.  Return the count.
-  //********************************************************************
-  size_t size() {
-    auto var_list = this;
-    size_t n = 0;
-    iterate(var_list) n++;
-    return n;
-  }
 };
 using LIST = list_rec *;

@@ -128,6 +118,8 @@ using LIST = list_rec *;
           Public Function Prototypes
 ----------------------------------------------------------------------*/

+size_t count(LIST var_list);
+
 LIST delete_d(LIST list, void *key, int_compare is_equal);

 TESS_API