From: ankur.tyagi85@gmail.com
To: openembedded-devel@lists.openembedded.org
Cc: Ankur Tyagi <ankur.tyagi85@gmail.com>
Subject: [oe][meta-oe][wrynose][PATCH 17/24] tesseract: patch CVE-2026-88048
Date: Thu, 24 Sep 2026 16:33:07 +1200 [thread overview]
Message-ID: <20260924043315.1663186-17-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-88048
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
.../tesseract/tesseract/CVE-2026-88048.patch | 228 ++++++++++++++++++
.../tesseract/tesseract_5.5.2.bb | 1 +
2 files changed, 229 insertions(+)
create mode 100644 meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-88048.patch
diff --git a/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-88048.patch b/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-88048.patch
new file mode 100644
index 0000000000..bc1beb5753
--- /dev/null
+++ b/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-88048.patch
@@ -0,0 +1,228 @@
+From 4dd118c5b87df1a4422ebb3ab3efaa3588b88cfa Mon Sep 17 00:00:00 2001
+From: Stefan Weil <sw@weilnetz.de>
+Date: Fri, 21 Aug 2026 15:23:59 +0200
+Subject: [PATCH] Validate FullyConnected weight matrix dimensions at load
+
+FullyConnected::DeSerialize read the WeightMatrix without checking
+that its dimensions match the layer's declared ni/no. MatrixDotVector
+drives the dot product from the matrix dimensions (writes w.dim1()
+results, reads w.dim2()-1 inputs) while the scratch buffers are sized
+from no_ and ni_, so a crafted .traineddata with a mismatched matrix
+performed a heap out-of-bounds write (up to 65535 rows) and out-of-
+bounds read on the first recognition step (crash, or heap corruption
+with attacker-influenced size and content).
+
+Key changes:
+- weightmatrix.h: add Dim1()/Dim2() accessors for the active weight
+ matrix (int or float), alongside the existing NumOutputs().
+- fullyconnected.cpp: reject the layer in DeSerialize unless
+ Dim1() == no_ and Dim2() == ni_ + 1 (the second dimension
+ includes the bias column), the layout that InitWeightsFloat
+ always produces.
+- unittest: new fullyconnected_test that builds a Softmax network
+ with mismatched matrix dimensions and expects CreateFromFile to
+ return nullptr; on unpatched code the test reaches Forward and
+ ASan catches the out-of-bounds write in MatrixDotVector. A
+ second test verifies that matching dimensions are still accepted.
+
+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 103dc134eb36411ddc6833ec20aa2c76795bd0ff)
+
+CVE: CVE-2026-88048
+Upstream-Status: Backport [https://github.com/tesseract-ocr/tesseract/commit/103dc134eb36411ddc6833ec20aa2c76795bd0ff]
+
+Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
+---
+ Makefile.am | 5 ++
+ src/lstm/fullyconnected.cpp | 11 ++-
+ src/lstm/weightmatrix.h | 7 ++
+ unittest/fullyconnected_test.cc | 115 ++++++++++++++++++++++++++++++++
+ 4 files changed, 137 insertions(+), 1 deletion(-)
+ create mode 100644 unittest/fullyconnected_test.cc
+
+diff --git a/Makefile.am b/Makefile.am
+index 1a0a6771..ea722409 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -1187,6 +1187,7 @@ if !DISABLED_LEGACY_ENGINE
+ check_PROGRAMS += equationdetect_test
+ endif # !DISABLED_LEGACY_ENGINE
+ check_PROGRAMS += fileio_test
++check_PROGRAMS += fullyconnected_test
+ check_PROGRAMS += heap_test
+ check_PROGRAMS += imagedata_test
+ if !DISABLED_LEGACY_ENGINE
+@@ -1319,6 +1320,10 @@ fileio_test_SOURCES = unittest/fileio_test.cc
+ fileio_test_CPPFLAGS = $(unittest_CPPFLAGS)
+ fileio_test_LDADD = $(TRAINING_LIBS)
+
++fullyconnected_test_SOURCES = unittest/fullyconnected_test.cc
++fullyconnected_test_CPPFLAGS = $(unittest_CPPFLAGS)
++fullyconnected_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/lstm/fullyconnected.cpp b/src/lstm/fullyconnected.cpp
+index 85989f40..380707a1 100644
+--- a/src/lstm/fullyconnected.cpp
++++ b/src/lstm/fullyconnected.cpp
+@@ -121,7 +121,16 @@ bool FullyConnected::Serialize(TFile *fp) const {
+
+ // Reads from the given file. Returns false in case of error.
+ bool FullyConnected::DeSerialize(TFile *fp) {
+- return weights_.DeSerialize(IsTraining(), fp);
++ if (!weights_.DeSerialize(IsTraining(), fp)) {
++ return false;
++ }
++ // The weight matrix must match the declared sizes (the second dimension
++ // includes the bias column); otherwise Forward would read or write
++ // outside the scratch buffers sized from ni_ and no_.
++ if (weights_.Dim1() != no_ || weights_.Dim2() != ni_ + 1) {
++ return false;
++ }
++ return true;
+ }
+
+ // Runs forward propagation of activations on the input line.
+diff --git a/src/lstm/weightmatrix.h b/src/lstm/weightmatrix.h
+index a2cdaa52..66b69dfa 100644
+--- a/src/lstm/weightmatrix.h
++++ b/src/lstm/weightmatrix.h
+@@ -107,6 +107,13 @@ public:
+ int NumOutputs() const {
+ return int_mode_ ? wi_.dim1() : wf_.dim1();
+ }
++ // The dimensions of the active weight matrix (wi_ in int mode, else wf_).
++ int Dim1() const {
++ return int_mode_ ? wi_.dim1() : wf_.dim1();
++ }
++ int Dim2() const {
++ return int_mode_ ? wi_.dim2() : wf_.dim2();
++ }
+ // Provides one set of weights. Only used by peep weight maxpool.
+ const TFloat *GetWeights(int index) const {
+ return wf_[index];
+diff --git a/unittest/fullyconnected_test.cc b/unittest/fullyconnected_test.cc
+new file mode 100644
+index 00000000..f9697de4
+--- /dev/null
++++ b/unittest/fullyconnected_test.cc
+@@ -0,0 +1,115 @@
++///////////////////////////////////////////////////////////////////////
++// File: fullyconnected_test.cc
++// Description: Tests that a FullyConnected (softmax) network layer with
++// weight-matrix dimensions that do not match the declared
++// ni/no is rejected at load. Without the check,
++// MatrixDotVector writes w.dim1() results into a scratch
++// buffer sized from no_ and reads w.dim2()-1 inputs from
++// a buffer sized from ni_ (heap out-of-bounds write/read)
++// on the first recognition step.
++//
++// 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 "network.h" // for Network, NetworkType
++#include "networkio.h"
++#include "networkscratch.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 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_;
++};
++
++void PutDoubleLE(ByteWriter *w, double d) {
++ union {
++ double d;
++ uint64_t u;
++ } conv;
++ conv.d = d;
++ w->PutU32(static_cast<uint32_t>(conv.u & 0xFFFFFFFF));
++ w->PutU32(static_cast<uint32_t>(conv.u >> 32));
++}
++
++// Builds a serialized NT_SOFTMAX network: header with the given ni/no,
++// then a float-mode WeightMatrix with the given (corrupt) dimensions.
++// The matrix is stored as doubles on disk (see WeightMatrix::DeSerialize).
++std::vector<char> MakeSoftmaxNetwork(int ni, int no, int32_t dim1, int32_t dim2) {
++ ByteWriter w;
++ w.PutU8(static_cast<uint32_t>(NT_SOFTMAX));
++ w.PutU8(0); // training: TS_DISABLED
++ w.PutU8(0); // needs_to_backprop
++ w.PutU32(0); // network_flags
++ w.PutU32(static_cast<uint32_t>(ni));
++ w.PutU32(static_cast<uint32_t>(no));
++ w.PutU32(0); // num_weights (not cross-checked, kept consistent anyway)
++ w.PutU32(0); // name (empty string)
++ // WeightMatrix::DeSerialize:
++ w.PutU8(128); // mode: kDoubleFlag, float mode
++ w.PutS32(dim1);
++ w.PutS32(dim2);
++ PutDoubleLE(&w, 0.0); // empty_ cell
++ for (int32_t i = 0; i < dim1 * dim2; ++i) {
++ PutDoubleLE(&w, 0.0); // weight data
++ }
++ return w.data();
++}
++
++// A FullyConnected layer whose weight matrix does not match the declared
++// sizes must be rejected by CreateFromFile; on unpatched code the test
++// reaches Forward, where MatrixDotVector performs the out-of-bounds
++// write this regression test guards against.
++TEST(FullyconnectedTest, RejectsWeightMatrixDimensionMismatch) {
++ // ni_=no_=1 but dim1=3 (OOB write of 3 results into a 1-result buffer)
++ // and dim2=5 (OOB read of 4 inputs from a 1-input buffer).
++ std::vector<char> bytes = MakeSoftmaxNetwork(1, 1, 3, 5);
++ TFile fp;
++ ASSERT_TRUE(fp.Open(bytes.data(), bytes.size()));
++ Network *net = Network::CreateFromFile(&fp);
++ if (net == nullptr) {
++ return; // Fixed: the mismatched layer is rejected at load.
++ }
++ NetworkIO input;
++ input.Resize2d(false, /*width=*/1, /*num_features=*/1);
++ NetworkScratch scratch;
++ NetworkIO output;
++ net->Forward(false, input, nullptr, &scratch, &output);
++ delete net;
++ FAIL() << "crafted FullyConnected layer with mismatched weight matrix was accepted";
++}
++
++// Consistent dimensions must still be accepted.
++TEST(FullyconnectedTest, AcceptsMatchingDimensions) {
++ std::vector<char> bytes = MakeSoftmaxNetwork(1, 2, 2, 2);
++ TFile fp;
++ ASSERT_TRUE(fp.Open(bytes.data(), bytes.size()));
++ Network *net = Network::CreateFromFile(&fp);
++ ASSERT_NE(net, nullptr);
++ delete net;
++}
++
++} // 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 a80407b749..a8b3e55c7d 100644
--- a/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb
+++ b/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb
@@ -11,6 +11,7 @@ SRC_URI = "git://github.com/${BPN}-ocr/${BPN}.git;branch=main;protocol=https;tag
file://CVE-2026-73067-1.patch \
file://CVE-2026-73067-2.patch \
file://CVE-2026-88052.patch \
+ file://CVE-2026-88048.patch \
"
next prev 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 ` ankur.tyagi85 [this message]
2026-09-24 4:33 ` [oe][meta-oe][wrynose][PATCH 18/24] tesseract: patch CVE-2026-88049 ankur.tyagi85
2026-09-24 4:33 ` [oe][meta-oe][wrynose][PATCH 19/24] tesseract: patch CVE-2026-88050 ankur.tyagi85
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-17-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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.