mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-10-22 20:38:28 +00:00
tesseract: patch CVE-2026-88051
Details:
https://nvd.nist.gov/vuln/detail/cve-2026-88051
(cherry picked from commit 727354dae2)
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
Signed-off-by: Khem Raj <khem.raj@oss.qualcomm.com>
This commit is contained in:
1 parent
2ba3ed5dc8
commit
4c928862f5
2 files changed
+185
No files matched your search
@@ -0,0 +1,184 @@
|
|||||||
|
From 79bf602b0cd5216c125f86eaf3c768daae256930 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 cc748455..c67f0f63 100644
|
||||||
|
--- a/Makefile.am
|
||||||
|
+++ b/Makefile.am
|
||||||
|
@@ -1159,6 +1159,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
|
||||||
|
@@ -1298,6 +1299,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
|
||||||
@@ -12,6 +12,7 @@ SRC_URI = "git://github.com/${BPN}-ocr/${BPN}.git;branch=main;protocol=https;tag
|
|||||||
file://CVE-2026-88049.patch \
|
file://CVE-2026-88049.patch \
|
||||||
file://CVE-2026-88050.patch \
|
file://CVE-2026-88050.patch \
|
||||||
file://CVE-2026-88047.patch \
|
file://CVE-2026-88047.patch \
|
||||||
|
file://CVE-2026-88051.patch \
|
||||||
"
|
"
|
||||||
|
|
||||||
DEPENDS = "leptonica"
|
DEPENDS = "leptonica"
|
||||||
|
|||||||
Reference in new issue
Block a user