diff --git a/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-88051.patch b/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-88051.patch
new file mode 100644
index 0000000000..efc204dc04
--- /dev/null
+++ b/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-88051.patch
@@ -0,0 +1,185 @@
+From 0ad773414bcb9a4f41e7d86759353d9558ba8cbe Mon Sep 17 00:00:00 2001
+From: Stefan Weil <sw@weilnetz.de>
+Date: Fri, 21 Aug 2026 20:29:37 +0200
+Subject: [PATCH] Validate vector counts in GenericVector::read
+
+The callback form of GenericVector::read read two independent int32
+fields from the file: reserved sized the allocation via reserve(),
+while size_used_ drove the element loop. Neither was capped and no
+size_used_ <= reserved invariant was checked, so a crafted
+.traineddata (e.g. the fontinfo table of a version >= 4 inttemp
+component) performed a heap out-of-bounds write during legacy
+engine initialization.
+
+Key changes:
+- genericvector.h: reject negative or over-limit reserved
+  (matching the 50000000 cap of the DeSerialize overloads) and
+  reject size_used_ < 0 or size_used_ > reserved before entering
+  the read loop. Legit files always satisfy size_used_ <= reserved,
+  as write() persists size_reserved_ first.
+- unittest: add genericvector_test covering size_used_ beyond
+  reserved (on unpatched code the ASan build dies on a
+  heap-buffer-overflow in the read loop), negative counts, and a
+  consistent vector that must still load.
+
+Reported-by: Zhixi "Jace" Sun <g.mygenie@gmail.com>
+Assisted-by: OpenCode / qwen3.8-27b-thinking (Alibaba Cloud)
+Signed-off-by: Stefan Weil <sw@weilnetz.de>
+(cherry picked from commit 56e09ca12e751623fe796ce1554ce704bffd2ef0)
+
+CVE: CVE-2026-88051
+Upstream-Status: Backport [https://github.com/tesseract-ocr/tesseract/commit/56e09ca12e751623fe796ce1554ce704bffd2ef0]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ Makefile.am                    |  5 ++
+ src/ccutil/genericvector.h     | 10 ++++
+ unittest/genericvector_test.cc | 91 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 106 insertions(+)
+ create mode 100644 unittest/genericvector_test.cc
+
+diff --git a/Makefile.am b/Makefile.am
+index 48e7dcbc..d2b503d4 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -1188,6 +1188,7 @@ check_PROGRAMS += equationdetect_test
+ endif # !DISABLED_LEGACY_ENGINE
+ check_PROGRAMS += fileio_test
+ check_PROGRAMS += fullyconnected_test
++check_PROGRAMS += genericvector_test
+ check_PROGRAMS += heap_test
+ check_PROGRAMS += imagedata_test
+ if !DISABLED_LEGACY_ENGINE
+@@ -1327,6 +1328,10 @@ fullyconnected_test_SOURCES = unittest/fullyconnected_test.cc
+ fullyconnected_test_CPPFLAGS = $(unittest_CPPFLAGS)
+ fullyconnected_test_LDADD = $(TESS_LIBS)
+ 
++genericvector_test_SOURCES = unittest/genericvector_test.cc
++genericvector_test_CPPFLAGS = $(unittest_CPPFLAGS)
++genericvector_test_LDADD = $(TESS_LIBS)
++
+ heap_test_SOURCES = unittest/heap_test.cc
+ heap_test_CPPFLAGS = $(unittest_CPPFLAGS)
+ heap_test_LDADD = $(TESS_LIBS)
+diff --git a/src/ccutil/genericvector.h b/src/ccutil/genericvector.h
+index 4a5bbe12..cbe1e203 100644
+--- a/src/ccutil/genericvector.h
++++ b/src/ccutil/genericvector.h
+@@ -654,10 +654,20 @@ bool GenericVector<T>::read(TFile *f, const std::function<bool(TFile *, T *)> &c
+   if (f->FReadEndian(&reserved, sizeof(reserved), 1) != 1) {
+     return false;
+   }
++  // Arbitrarily limit the number of elements to protect against bad data.
++  const uint32_t limit = 50000000;
++  if (reserved < 0 || static_cast<uint32_t>(reserved) > limit) {
++    return false;
++  }
+   reserve(reserved);
+   if (f->FReadEndian(&size_used_, sizeof(size_used_), 1) != 1) {
+     return false;
+   }
++  // size_used_ is an independent file field; without this check the reads
++  // below land past the end of the buffer sized from reserved.
++  if (size_used_ < 0 || size_used_ > reserved) {
++    return false;
++  }
+   if (cb != nullptr) {
+     for (int i = 0; i < size_used_; ++i) {
+       if (!cb(f, data_ + i)) {
+diff --git a/unittest/genericvector_test.cc b/unittest/genericvector_test.cc
+new file mode 100644
+index 00000000..269b00f0
+--- /dev/null
++++ b/unittest/genericvector_test.cc
+@@ -0,0 +1,91 @@
++///////////////////////////////////////////////////////////////////////
++// File:        genericvector_test.cc
++// Description: Tests that the callback form of GenericVector::read
++//              rejects vectors whose size_used_ exceeds reserved (or
++//              whose counts are negative). reserved sizes the buffer
++//              while size_used_ is an independent file field driving
++//              the element loop, so a crafted .traineddata (e.g. the
++//              fontinfo table of a version >= 4 inttemp component)
++//              performs a heap out-of-bounds write during legacy
++//              engine initialization.
++//
++// Licensed under the Apache License, Version 2.0 (the "License");
++// you may not use this file except in compliance with the License.
++// You may obtain a copy of the License at
++// http://www.apache.org/licenses/LICENSE-2.0
++//
++///////////////////////////////////////////////////////////////////////
++
++#include "include_gunit.h"
++
++#include "genericvector.h"
++#include "serialis.h" // for TFile
++
++#include <cstdint>
++#include <vector>
++
++namespace tesseract {
++namespace {
++
++// Appends raw little-endian values to a byte buffer.
++class ByteWriter {
++public:
++  void PutS32(int32_t v) {
++    uint32_t u = static_cast<uint32_t>(v);
++    for (int i = 0; i < 4; ++i) {
++      data_.push_back(static_cast<char>((u >> (8 * i)) & 0xFF));
++    }
++  }
++  const std::vector<char> &data() const { return data_; }
++
++private:
++  std::vector<char> data_;
++};
++
++// A serialized vector header (reserved, size_used_) followed by the
++// given number of int32 elements.
++std::vector<char> MakeVector(int32_t reserved, int32_t size_used, int32_t num_elements) {
++  ByteWriter w;
++  w.PutS32(reserved);
++  w.PutS32(size_used);
++  for (int32_t i = 0; i < num_elements; ++i) {
++    w.PutS32(i);
++  }
++  return w.data();
++}
++
++// reserved=4 but size_used_=0x10000: on unpatched code the callback
++// loop writes 65536 ints past the 4-int buffer (heap out-of-bounds
++// write).
++TEST(GenericVectorTest, RejectsSizeUsedBeyondReserved) {
++  std::vector<char> bytes = MakeVector(4, 0x10000, 0x10000);
++  TFile fp;
++  ASSERT_TRUE(fp.Open(bytes.data(), bytes.size()));
++  GenericVector<int> v;
++  EXPECT_FALSE(v.read(&fp, [](TFile *f, int *p) { return f->DeSerialize(p); }));
++}
++
++// Negative counts must be rejected; on unpatched code the read
++// "succeeds" and leaves size_used_ negative.
++TEST(GenericVectorTest, RejectsNegativeCounts) {
++  std::vector<char> bytes = MakeVector(-1, -1, 0);
++  TFile fp;
++  ASSERT_TRUE(fp.Open(bytes.data(), bytes.size()));
++  GenericVector<int> v;
++  EXPECT_FALSE(v.read(&fp, [](TFile *f, int *p) { return f->DeSerialize(p); }));
++}
++
++// A consistent vector must still be accepted.
++TEST(GenericVectorTest, AcceptsConsistentVector) {
++  std::vector<char> bytes = MakeVector(4, 2, 2);
++  TFile fp;
++  ASSERT_TRUE(fp.Open(bytes.data(), bytes.size()));
++  GenericVector<int> v;
++  ASSERT_TRUE(v.read(&fp, [](TFile *f, int *p) { return f->DeSerialize(p); }));
++  EXPECT_EQ(v.size(), 2);
++  EXPECT_EQ(v[0], 0);
++  EXPECT_EQ(v[1], 1);
++}
++
++} // namespace
++} // namespace tesseract
diff --git a/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb b/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb
index 61cb1f7cad..60b50f16a5 100644
--- a/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb
+++ b/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb
@@ -15,6 +15,7 @@ SRC_URI = "git://github.com/${BPN}-ocr/${BPN}.git;branch=main;protocol=https;tag
            file://CVE-2026-88049.patch \
            file://CVE-2026-88050.patch \
            file://CVE-2026-88047.patch \
+           file://CVE-2026-88051.patch \
 "
 
 
