diff --git a/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-73066.patch b/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-73066.patch new file mode 100644 index 0000000000..fc762ba8ca --- /dev/null +++ b/meta-oe/recipes-graphics/tesseract/tesseract/CVE-2026-73066.patch @@ -0,0 +1,152 @@ +From f66bfcc45d1acd3e3c98f81e1edc8ddf49786555 Mon Sep 17 00:00:00 2001 +From: Stefan Weil +Date: Thu, 23 Jul 2026 18:05:39 +0200 +Subject: [PATCH] Fix integer overflow in LSTM Convolve and Reconfig + deserialization (#4588) + +Add range and overflow validation in Convolve::DeSerialize and +Reconfig::DeSerialize to prevent a crafted .traineddata file from +triggering a heap out-of-bounds write via unchecked signed integer +multiplication when computing the output-channel count. + +Validate ni/no/num_weights in Network::CreateFromFile. + +Add defense-in-depth bounds assertions in NetworkIO::Randomize and +NetworkIO::CopyTimeStepGeneral. + +Reported-by: Eunho Kim +Signed-off-by: Stefan Weil +Assisted-by: OpenCode / big-pickle (opencode) +Tested-by: Eunho Kim +(cherry picked from commit 2f4d2f4bf45c363785d7bf1da29b6628f8939a72) + +CVE: CVE-2026-73066 +Upstream-Status: Backport [https://github.com/tesseract-ocr/tesseract/commit/2f4d2f4bf45c363785d7bf1da29b6628f8939a72] + +Signed-off-by: Ankur Tyagi +--- + src/lstm/convolve.cpp | 24 +++++++++++++++++++++++- + src/lstm/network.cpp | 6 ++++++ + src/lstm/networkio.cpp | 2 ++ + src/lstm/reconfig.cpp | 20 +++++++++++++++++++- + 4 files changed, 50 insertions(+), 2 deletions(-) + +diff --git a/src/lstm/convolve.cpp b/src/lstm/convolve.cpp +index 6cfaa06e..d20a991c 100644 +--- a/src/lstm/convolve.cpp ++++ b/src/lstm/convolve.cpp +@@ -23,8 +23,11 @@ + + #include "convolve.h" + ++#include ++ + #include "networkscratch.h" + #include "serialis.h" ++#include "tprintf.h" + + namespace tesseract { + +@@ -46,7 +49,26 @@ bool Convolve::DeSerialize(TFile *fp) { + if (!fp->DeSerialize(&half_y_)) { + return false; + } +- no_ = ni_ * (2 * half_x_ + 1) * (2 * half_y_ + 1); ++ if (half_x_ < 0 || half_y_ < 0 || ni_ <= 0) { ++ tprintf("Error: invalid Convolve parameters: ni=%d half_x=%d half_y=%d\n", ni_, half_x_, ++ half_y_); ++ return false; ++ } ++ int64_t kx = 2LL * half_x_ + 1; ++ int64_t ky = 2LL * half_y_ + 1; ++ // Stepwise overflow check: ni_ * kx * ky must fit in int. ++ if (kx > INT_MAX / ky) { ++ tprintf("Error: Convolve output-channel count overflows: ni=%d half_x=%d half_y=%d\n", ni_, ++ half_x_, half_y_); ++ return false; ++ } ++ int64_t kxky = kx * ky; ++ if (static_cast(ni_) > INT_MAX / kxky) { ++ tprintf("Error: Convolve output-channel count overflows: ni=%d half_x=%d half_y=%d\n", ni_, ++ half_x_, half_y_); ++ return false; ++ } ++ no_ = static_cast(static_cast(ni_) * kxky); + return true; + } + +diff --git a/src/lstm/network.cpp b/src/lstm/network.cpp +index cfddbfd4..8230992a 100644 +--- a/src/lstm/network.cpp ++++ b/src/lstm/network.cpp +@@ -247,6 +247,12 @@ Network *Network::CreateFromFile(TFile *fp) { + return nullptr; + } + ++ if (ni < 0 || no < 0 || num_weights < 0) { ++ tprintf("Error: invalid network layer parameters: type=%d ni=%d no=%d num_weights=%d\n", type, ++ ni, no, num_weights); ++ return nullptr; ++ } ++ + switch (type) { + case NT_CONVOLVE: + network = new Convolve(name, ni, 0, 0); +diff --git a/src/lstm/networkio.cpp b/src/lstm/networkio.cpp +index 3cb068c6..8636075b 100644 +--- a/src/lstm/networkio.cpp ++++ b/src/lstm/networkio.cpp +@@ -405,6 +405,7 @@ void NetworkIO::CopyTimeStepFrom(int dest_t, const NetworkIO &src, int src_t) { + void NetworkIO::CopyTimeStepGeneral(int dest_t, int dest_offset, int num_features, + const NetworkIO &src, int src_t, int src_offset) { + ASSERT_HOST(int_mode_ == src.int_mode_); ++ ASSERT_HOST(dest_offset + num_features <= NumFeatures()); + if (int_mode_) { + memcpy(i_[dest_t] + dest_offset, src.i_[src_t] + src_offset, num_features * sizeof(i_[0][0])); + } else { +@@ -414,6 +415,7 @@ void NetworkIO::CopyTimeStepGeneral(int dest_t, int dest_offset, int num_feature + + // Sets the given range to random values. + void NetworkIO::Randomize(int t, int offset, int num_features, TRand *randomizer) { ++ ASSERT_HOST(offset + num_features <= NumFeatures()); + if (int_mode_) { + int8_t *line = i_[t] + offset; + for (int i = 0; i < num_features; ++i) { +diff --git a/src/lstm/reconfig.cpp b/src/lstm/reconfig.cpp +index 2f49d63e..a4e99497 100644 +--- a/src/lstm/reconfig.cpp ++++ b/src/lstm/reconfig.cpp +@@ -18,6 +18,10 @@ + + #include "reconfig.h" + ++#include ++ ++#include "tprintf.h" ++ + namespace tesseract { + + Reconfig::Reconfig(const std::string &name, int ni, int x_scale, int y_scale) +@@ -60,7 +64,21 @@ bool Reconfig::DeSerialize(TFile *fp) { + if (!fp->DeSerialize(&y_scale_)) { + return false; + } +- no_ = ni_ * x_scale_ * y_scale_; ++ if (x_scale_ <= 0 || y_scale_ <= 0 || ni_ <= 0) { ++ tprintf("Error: invalid Reconfig parameters: ni=%d x_scale=%d y_scale=%d\n", ni_, x_scale_, ++ y_scale_); ++ return false; ++ } ++ int64_t xs = x_scale_; ++ int64_t ys = y_scale_; ++ // Stepwise overflow check: ni_ * x_scale_ * y_scale_ must fit in int. ++ int64_t xsys = xs * ys; ++ if (static_cast(ni_) > INT_MAX / xsys) { ++ tprintf("Error: Reconfig output-channel count overflows: ni=%d x_scale=%d y_scale=%d\n", ni_, ++ x_scale_, y_scale_); ++ return false; ++ } ++ no_ = static_cast(static_cast(ni_) * xsys); + return true; + } + 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 46b789cbc4..1b5a5fe2df 100644 --- a/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb +++ b/meta-oe/recipes-graphics/tesseract/tesseract_5.5.2.bb @@ -6,7 +6,9 @@ LICENSE = "Apache-2.0" LIC_FILES_CHKSUM = "file://LICENSE;md5=3b83ef96387f14655fc854ddc3c6bd57" SRCREV = "6e1d56a847e697de07b38619356550e5cf4e8633" -SRC_URI = "git://github.com/${BPN}-ocr/${BPN}.git;branch=main;protocol=https;tag=${PV}" +SRC_URI = "git://github.com/${BPN}-ocr/${BPN}.git;branch=main;protocol=https;tag=${PV} \ + file://CVE-2026-73066.patch \ +" DEPENDS = "leptonica"