Commit 3624c6bf for tesseract
commit 3624c6bf224719db284d97ccd3360d1e419254c0
Author: Stefan Weil <sw@weilnetz.de>
Date: Fri Nov 13 17:30:19 2026 +0100
textord: fix loop counters and a division by zero in GAPMAP
- The loops over the gap map counted with an int16_t and tested
i <= map_max. For map_max == INT16_MAX (a block which is 32767
pixels wide with a bucket size of 1) that test never fails: the
counter wraps, and the loop writes in front of the map and does not
end in builds with g++. Use int for the counters.
- The x-height of a row was narrowed to int16_t before it was added to
the histogram. Values above 32767, which are possible with a large
textord_min_xheight, became small or negative instead of being
clipped to the range of the histogram by STATS::add(). That is how
the bucket size got as small as 1 or 0.
- A median x-height below 2 gave a bucket size of 0 and a division by
zero. Use a bucket size of at least 1.
The first two changes are the fix which was proposed by the scanner,
the third one was suggested by its review.
Based on the fix proposed by the Anthropic OSS Scanner
(finding ANT-2026-R518F4VZ).
Assisted-by: OpenCode / Claude Fable 5.2 (Anthropic)
Signed-off-by: Stefan Weil <sw@weilnetz.de>
diff --git a/src/textord/gap_map.cpp b/src/textord/gap_map.cpp
index 4c20d117..2f460d59 100644
--- a/src/textord/gap_map.cpp
+++ b/src/textord/gap_map.cpp
@@ -46,7 +46,7 @@ GAPMAP::GAPMAP( // Constructor
STATS xht_stats(0, 127);
int16_t min_quantum;
int16_t max_quantum;
- int16_t i;
+ int i; // wider than map_max, otherwise i <= map_max never fails at INT16_MAX
/*
Find left and right extremes and bucket size
@@ -63,7 +63,7 @@ GAPMAP::GAPMAP( // Constructor
row = row_it.data();
if (!row->blob_list()->empty()) {
total_rows++;
- xht_stats.add(static_cast<int16_t>(floor(row->xheight + 0.5)), 1);
+ xht_stats.add(static_cast<int32_t>(floor(row->xheight + 0.5)), 1);
blob_it.set_to_list(row->blob_list());
start_of_row = blob_it.data()->bounding_box().left();
end_of_row = blob_it.data_relative(-1)->bounding_box().right();
@@ -83,6 +83,9 @@ GAPMAP::GAPMAP( // Constructor
return;
}
bucket_size = static_cast<int16_t>(floor(xht_stats.median() + 0.5)) / 2;
+ if (bucket_size < 1) {
+ bucket_size = 1; // avoid a division by zero for a median x-height below 2
+ }
map_max = (max_right - min_left) / bucket_size;
map = new int16_t[map_max + 1];
for (i = 0; i <= map_max; i++) {
@@ -169,7 +172,7 @@ bool GAPMAP::table_gap( // Is gap a table?
) {
int16_t min_quantum;
int16_t max_quantum;
- int16_t i;
+ int i; // wider than map_max, otherwise i <= max_quantum never fails at INT16_MAX
bool tab_found = false;
if (!any_tabs) {