Commit 289f7266 for tesseract
commit 289f72664cb51a1476a0869cb027fe53be1f3e01
Author: Stefan Weil <sw@weilnetz.de>
Date: Sun Jul 12 21:28:34 2026 +0200
Fix O(n²) forward-edge validation and TFile::Skip overflow
- dawg.cpp: restructure forward-edge run validation to validate each
edge in the run in a single pass and advance the outer loop to the
terminator, reducing worst-case from O(n²) to O(n).
- serialis.cpp: replace addition-based bounds check in Skip with a
subtraction-based check to avoid overflow when offset_ + count
wraps. Clamp offset_ to UINT_MAX when data exceeds 32-bit range.
Assisted-by: OpenCode / big-pickle (opencode)
Reported-by: GitHub Copilot
Signed-off-by: Stefan Weil <stweil@tessus.org>
diff --git a/src/ccutil/serialis.cpp b/src/ccutil/serialis.cpp
index f750823a..b8914c0b 100644
--- a/src/ccutil/serialis.cpp
+++ b/src/ccutil/serialis.cpp
@@ -143,9 +143,13 @@ bool TFile::Serialize(const std::vector<char> &data) {
}
bool TFile::Skip(size_t count) {
- if (data_ != nullptr && offset_ + count > data_->size()) {
- offset_ = data_->size();
- return false;
+ if (data_ != nullptr) {
+ size_t data_size = data_->size();
+ // Subtraction-based check to avoid overflow in offset_ + count.
+ if (offset_ >= data_size || count > data_size - offset_) {
+ offset_ = data_size > UINT_MAX ? UINT_MAX : static_cast<unsigned>(data_size);
+ return false;
+ }
}
offset_ += count;
return true;
diff --git a/src/dict/dawg.cpp b/src/dict/dawg.cpp
index f386dd9d..93a18f6e 100644
--- a/src/dict/dawg.cpp
+++ b/src/dict/dawg.cpp
@@ -391,17 +391,19 @@ bool SquishedDawg::read_squished_dawg(TFile *file) {
if (edges_[i] == next_node_mask_) {
continue; // Empty slot.
}
- NODE_REF next = next_node_from_edge_rec(edges_[i]);
- if (next != 0 && static_cast<uint32_t>(next) >= num_edges_) {
- tprintf("Dawg edge %u has out-of-bounds next_node\n", i);
- return false;
- }
if (forward_edge(i)) {
- uint32_t j = i;
+ // Validate the entire forward-edge run and skip to its terminator.
bool terminated = false;
+ uint32_t j = i;
do {
+ NODE_REF nj = next_node_from_edge_rec(edges_[j]);
+ if (nj != 0 && static_cast<uint32_t>(nj) >= num_edges_) {
+ tprintf("Dawg edge %u has out-of-bounds next_node\n", j);
+ return false;
+ }
if (last_edge(j)) {
terminated = true;
+ i = j; // Advance outer loop past the terminator.
break;
}
++j;
@@ -410,6 +412,12 @@ bool SquishedDawg::read_squished_dawg(TFile *file) {
tprintf("Dawg forward edge run starting at %u is not terminated\n", i);
return false;
}
+ } else {
+ NODE_REF next = next_node_from_edge_rec(edges_[i]);
+ if (next != 0 && static_cast<uint32_t>(next) >= num_edges_) {
+ tprintf("Dawg edge %u has out-of-bounds next_node\n", i);
+ return false;
+ }
}
}
if (debug_level_ > 2) {