Openembedded Devel Discussions
 help / color / mirror / Atom feed
From: ankur.tyagi85@gmail.com
To: openembedded-devel@lists.openembedded.org
Cc: Ankur Tyagi <ankur.tyagi85@gmail.com>
Subject: [oe][meta-oe][wrynose][PATCH 19/24] tesseract: patch CVE-2026-88050
Date: Thu, 24 Sep 2026 16:33:09 +1200	[thread overview]
Message-ID: <20260924043315.1663186-19-ankur.tyagi85@gmail.com> (raw)
In-Reply-To: <20260924043315.1663186-1-ankur.tyagi85@gmail.com>

From: Ankur Tyagi <ankur.tyagi85@gmail.com>

Details:
https://nvd.nist.gov/vuln/detail/cve-2026-88050

Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
 .../tesseract/tesseract/CVE-2026-88050.patch  | 210 ++++++++++++++++++
 .../tesseract/tesseract_5.5.2.bb              |   1 +
 2 files changed, 211 insertions(+)
 create mode 100644 meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-88050.patch

diff --git a/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-88050.patch b/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-88050.patch
new file mode 100644
index 0000000000..b07ef69989
--- /dev/null
+++ b/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-88050.patch
@@ -0,0 +1,210 @@
+From bb1e72a7f8488d653d2a37e482babac6d9dc7ab2 Mon Sep 17 00:00:00 2001
+From: Stefan Weil <sw@weilnetz.de>
+Date: Fri, 21 Aug 2026 16:56:51 +0200
+Subject: [PATCH] Reject recoder code values outside the sane range at load
+
+RecodedCharID::DeSerialize (hardened by 82727cc to check length_
+only) still read the individual code values as raw signed int32.
+UnicharCompress::ComputeCodeRange computes code_range_ as 1 plus the
+maximum code using a signed > comparison, so a code value of -1
+never raises the maximum and yields code_range_ = 0. SetupDecoder
+then resizes is_valid_start_ to 0 and writes is_valid_start_[code(0)]
+on the size-0 vector<bool>, an out-of-bounds write at a wild wrapped
+index (deterministic crash) on LSTMRecognizer load. A code value of
+INT32_MAX instead wraps code_range_ negative and makes resize()
+throw.
+
+Key changes:
+- unicharcompress.h: validate each deserialized code value to be
+  within [0, UINT16_MAX), the same arbitrary cap used elsewhere for
+  .traineddata counts; reject the recoder otherwise.
+- unicharcompress.h/.cpp: add defense-in-depth bounds assertions to
+  IsValidFirstCode and SetupDecoder, matching the style of the
+  NetworkIO assertions from 2f4d2f4.
+- unittest: add recoder_test, which feeds a crafted UnicharCompress
+  with a -1 code and an INT32_MAX code and expects DeSerialize to
+  fail (on unpatched code the -1 case dies on the SEGV in
+  SetupDecoder, the huge case on the uncaught length_error), plus a
+  positive control that valid codes load and answer
+  code_range()/IsValidFirstCode() correctly.
+
+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 c94a5532ee04db5a4919542832fd94caee5ea58f)
+
+CVE: CVE-2026-88050
+Upstream-Status: Backport [https://github.com/tesseract-ocr/tesseract/commit/c94a5532ee04db5a4919542832fd94caee5ea58f]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ Makefile.am                    |  5 ++
+ src/ccutil/unicharcompress.cpp |  1 +
+ src/ccutil/unicharcompress.h   | 13 ++++-
+ unittest/recoder_test.cc       | 91 ++++++++++++++++++++++++++++++++++
+ 4 files changed, 109 insertions(+), 1 deletion(-)
+ create mode 100644 unittest/recoder_test.cc
+
+diff --git a/Makefile.am b/Makefile.am
+index 86ac9d0d..c1491263 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -1231,6 +1231,7 @@ endif # !DISABLED_LEGACY_ENGINE
+ check_PROGRAMS += progress_test
+ check_PROGRAMS += qrsequence_test
+ check_PROGRAMS += recodebeam_test
++check_PROGRAMS += recoder_test
+ check_PROGRAMS += rect_test
+ check_PROGRAMS += resultiterator_test
+ check_PROGRAMS += scanutils_test
+@@ -1464,6 +1465,10 @@ recodebeam_test_SOURCES = unittest/recodebeam_test.cc
+ recodebeam_test_CPPFLAGS = $(unittest_CPPFLAGS)
+ recodebeam_test_LDADD = $(TRAINING_LIBS) $(ICU_I18N_LIBS) $(ICU_UC_LIBS)
+ 
++recoder_test_SOURCES = unittest/recoder_test.cc
++recoder_test_CPPFLAGS = $(unittest_CPPFLAGS)
++recoder_test_LDADD = $(TESS_LIBS)
++
+ rect_test_SOURCES = unittest/rect_test.cc
+ rect_test_CPPFLAGS = $(unittest_CPPFLAGS)
+ rect_test_LDADD = $(TESS_LIBS)
+diff --git a/src/ccutil/unicharcompress.cpp b/src/ccutil/unicharcompress.cpp
+index d5efccab..7f3d48fe 100644
+--- a/src/ccutil/unicharcompress.cpp
++++ b/src/ccutil/unicharcompress.cpp
+@@ -400,6 +400,7 @@ void UnicharCompress::SetupDecoder() {
+   for (unsigned c = 0; c < encoder_.size(); ++c) {
+     const RecodedCharID &code = encoder_[c];
+     decoder_[code] = c;
++    ASSERT_HOST(code(0) >= 0 && code(0) < code_range_);
+     is_valid_start_[code(0)] = true;
+     RecodedCharID prefix = code;
+     int len = code.length() - 1;
+diff --git a/src/ccutil/unicharcompress.h b/src/ccutil/unicharcompress.h
+index 67a441e8..05778ce6 100644
+--- a/src/ccutil/unicharcompress.h
++++ b/src/ccutil/unicharcompress.h
+@@ -79,7 +79,17 @@ public:
+     if (length_ > kMaxCodeLen) {
+       return false;
+     }
+-    return fp->DeSerialize(&code_[0], length_);
++    if (!fp->DeSerialize(&code_[0], length_)) {
++      return false;
++    }
++    // Code values index arrays sized from the maximum code; reject values
++    // that are out of the sane range for a recoded alphabet.
++    for (uint32_t i = 0; i < length_; ++i) {
++      if (code_[i] < 0 || code_[i] >= static_cast<int32_t>(UINT16_MAX)) {
++        return false;
++      }
++    }
++    return true;
+   }
+   bool operator==(const RecodedCharID &other) const {
+     if (length_ != other.length_) {
+@@ -185,6 +195,7 @@ public:
+   int DecodeUnichar(const RecodedCharID &code) const;
+   // Returns true if the given code is a valid start or single code.
+   bool IsValidFirstCode(int code) const {
++    ASSERT_HOST(code >= 0 && code < code_range_);
+     return is_valid_start_[code];
+   }
+   // Returns a list of valid non-final next codes for a given prefix code,
+diff --git a/unittest/recoder_test.cc b/unittest/recoder_test.cc
+new file mode 100644
+index 00000000..48dfa7ea
+--- /dev/null
++++ b/unittest/recoder_test.cc
+@@ -0,0 +1,91 @@
++///////////////////////////////////////////////////////////////////////
++// File:        recoder_test.cc
++// Description: Tests that a UnicharCompress (LSTM recoder) with code
++//              values outside the sane range is rejected at load.
++//              Negative code values leave code_range_ at zero, so
++//              SetupDecoder writes is_valid_start_[code(0)] out of
++//              bounds on a size-0 vector<bool>; huge code values wrap
++//              code_range_ and make resize() throw.
++//
++// 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 "serialis.h" // for TFile
++#include "unicharcompress.h"
++
++#include <cstdint>
++#include <vector>
++
++namespace tesseract {
++namespace {
++
++// Appends raw little-endian values to a byte buffer.
++class ByteWriter {
++public:
++  void PutU8(uint32_t v) { data_.push_back(static_cast<char>(v & 0xFF)); }
++  void PutU32(uint32_t v) {
++    for (int i = 0; i < 4; ++i) {
++      data_.push_back(static_cast<char>((v >> (8 * i)) & 0xFF));
++    }
++  }
++  void PutS32(int32_t v) { PutU32(static_cast<uint32_t>(v)); }
++  const std::vector<char> &data() const { return data_; }
++
++private:
++  std::vector<char> data_;
++};
++
++// A serialized UnicharCompress with one length-1 RecodedCharID per
++// given code value (self-normalizing).
++std::vector<char> MakeRecoder(const std::vector<int32_t> &codes) {
++  ByteWriter w;
++  w.PutU32(codes.size());
++  for (int32_t code : codes) {
++    w.PutU8(1); // self_normalized_
++    w.PutU32(1); // length_
++    w.PutS32(code); // code_[0]
++  }
++  return w.data();
++}
++
++// A recoder code of -1 keeps code_range_ at 0, so on unpatched code
++// SetupDecoder performs an out-of-bounds write into the size-0
++// is_valid_start_ vector<bool>.
++TEST(RecoderTest, RejectsNegativeCode) {
++  std::vector<char> bytes = MakeRecoder({-1});
++  TFile fp;
++  ASSERT_TRUE(fp.Open(bytes.data(), bytes.size()));
++  UnicharCompress recoder;
++  EXPECT_FALSE(recoder.DeSerialize(&fp));
++}
++
++// A recoder code of INT32_MAX wraps code_range_ to a negative value,
++// so on unpatched code SetupDecoder's resize() throws.
++TEST(RecoderTest, RejectsHugeCode) {
++  std::vector<char> bytes = MakeRecoder({INT32_MAX});
++  TFile fp;
++  ASSERT_TRUE(fp.Open(bytes.data(), bytes.size()));
++  UnicharCompress recoder;
++  EXPECT_FALSE(recoder.DeSerialize(&fp));
++}
++
++// A valid recoder must still be accepted and usable.
++TEST(RecoderTest, AcceptsValidCodes) {
++  std::vector<char> bytes = MakeRecoder({0, 1});
++  TFile fp;
++  ASSERT_TRUE(fp.Open(bytes.data(), bytes.size()));
++  UnicharCompress recoder;
++  ASSERT_TRUE(recoder.DeSerialize(&fp));
++  EXPECT_EQ(recoder.code_range(), 2);
++  EXPECT_TRUE(recoder.IsValidFirstCode(0));
++  EXPECT_TRUE(recoder.IsValidFirstCode(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 df6ff11d78..756e780659 100644
--- a/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb
+++ b/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb
@@ -13,6 +13,7 @@ SRC_URI = "git://github.com/${BPN}-ocr/${BPN}.git;branch=main;protocol=https;tag
            file://CVE-2026-88052.patch \
            file://CVE-2026-88048.patch \
            file://CVE-2026-88049.patch \
+           file://CVE-2026-88050.patch \
 "
 
 


  parent reply	other threads:[~2026-09-24  4:34 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-24  4:32 [oe][meta-oe][wrynose][PATCH 1/24] rabbitmq-c: patch CVE-2026-44235 ankur.tyagi85
2026-09-24  4:32 ` [oe][meta-oe][wrynose][PATCH 2/24] rabbitmq-c: patch CVE-2026-44236 ankur.tyagi85
2026-09-24  4:32 ` [oe][meta-oe][wrynose][PATCH 3/24] redis: patch CVE-2026-81934 ankur.tyagi85
2026-09-24  4:32 ` [oe][meta-networking][wrynose][PATCH 4/24] sngrep: patch CVE-2026-90558 ankur.tyagi85
2026-09-24  4:32 ` [oe][meta-networking][wrynose][PATCH 5/24] strongswan: patch CVE-2026-78123 ankur.tyagi85
2026-09-24  4:32 ` [oe][meta-networking][wrynose][PATCH 6/24] strongswan: patch CVE-2026-78124 ankur.tyagi85
2026-09-24  4:32 ` [oe][meta-networking][wrynose][PATCH 7/24] strongswan: patch CVE-2026-78126 ankur.tyagi85
2026-09-24  4:32 ` [oe][meta-networking][wrynose][PATCH 8/24] strongswan: patch CVE-2026-78127 ankur.tyagi85
2026-09-24  4:32 ` [oe][meta-networking][wrynose][PATCH 9/24] strongswan: patch CVE-2026-78129 ankur.tyagi85
2026-09-24  4:33 ` [oe][meta-networking][wrynose][PATCH 10/24] strongswan: patch CVE-2026-78133 ankur.tyagi85
2026-09-24  4:33 ` [oe][meta-networking][wrynose][PATCH 11/24] strongswan: patch CVE-2026-78131 ankur.tyagi85
2026-09-24  4:33 ` [oe][meta-networking][wrynose][PATCH 12/24] strongswan: patch CVE-2026-78130 ankur.tyagi85
2026-10-05  9:49   ` Anuj Mittal
2026-10-06 20:25     ` Ankur Tyagi
2026-09-24  4:33 ` [oe][meta-networking][wrynose][PATCH 13/24] strongswan: patch CVE-2026-78132 ankur.tyagi85
2026-09-24  4:33 ` [oe][meta-networking][wrynose][PATCH 14/24] strongswan: patch CVE-2026-78135 ankur.tyagi85
2026-09-24  4:33 ` [oe][meta-networking][wrynose][PATCH 15/24] strongswan: patch CVE-2026-78134 ankur.tyagi85
2026-09-24  4:33 ` [oe][meta-oe][wrynose][PATCH 16/24] tesseract: patch CVE-2026-88052 ankur.tyagi85
2026-09-24  4:33 ` [oe][meta-oe][wrynose][PATCH 17/24] tesseract: patch CVE-2026-88048 ankur.tyagi85
2026-09-24  4:33 ` [oe][meta-oe][wrynose][PATCH 18/24] tesseract: patch CVE-2026-88049 ankur.tyagi85
2026-09-24  4:33 ` ankur.tyagi85 [this message]
2026-09-24  4:33 ` [oe][meta-oe][wrynose][PATCH 20/24] tesseract: patch CVE-2026-88047 ankur.tyagi85
2026-09-24  4:33 ` [oe][meta-oe][wrynose][PATCH 21/24] tesseract: patch CVE-2026-88051 ankur.tyagi85
2026-09-24  4:33 ` [oe][meta-oe][wrynose][PATCH 22/24] tesseract: patch CVE-2026-88054 ankur.tyagi85
2026-09-24  4:33 ` [oe][meta-oe][wrynose][PATCH 23/24] valkey: upgrade 9.0.5 -> 9.0.6 ankur.tyagi85
2026-09-24  4:33 ` [oe][meta-oe][wrynose][PATCH 24/24] valkey: mark CVE-2026-86227 fixed ankur.tyagi85
2026-10-06  4:19 ` [oe][meta-oe][wrynose][PATCH 1/24] rabbitmq-c: patch CVE-2026-44235 Anuj Mittal
2026-10-06 20:27   ` Ankur Tyagi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260924043315.1663186-19-ankur.tyagi85@gmail.com \
    --to=ankur.tyagi85@gmail.com \
    --cc=openembedded-devel@lists.openembedded.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox