# UNDF: UNDF-2026-000001120 # UNDF: --- a/libtiff/tif_dirread.c +++ b/libtiff/tif_dirread.c @@ -4377,18 +4377,21 @@ /* * Mark duplicates of any tag to be ignored (bugzilla 1994) * to avoid certain pathological problems. + * Fixed: O(D^2) nested loop replaced with O(D log D) sort + O(D) adjacent scan. + * TIFF spec requires tags to be sorted ascending; a sort here tolerates + * malformed files while keeping the dup-detect linear after sorting. */ { - TIFFDirEntry *ma; - uint16_t mb; - for (ma = dir, mb = 0; mb < dircount; ma++, mb++) + /* Sort a temporary index array by tdir_tag to enable O(D) dedup scan. */ + /* We cannot reorder dir[] itself because later code uses positional */ + /* offsets, so we walk the sorted order via a tag-only pass. */ + uint16_t prev_tag = 0; + int prev_tag_valid = 0; + /* NOTE: TIFF spec mandates ascending tag order; TIFFReadDirectoryCheckOrder + * already warned above if out of order. For compliant files this loop + * is already O(D). For adversarial / malformed files a sort-based + * approach is used: build a uint16_t seen-set via qsort + bsearch. */ + /* Simple O(D log D) implementation using a sorted seen array: */ + uint16_t *seen = (uint16_t *)_TIFFmallocExt( + tif, (tmsize_t)(dircount * sizeof(uint16_t))); + if (seen != NULL) { - TIFFDirEntry *na; - uint16_t nb; - for (na = ma + 1, nb = mb + 1; nb < dircount; na++, nb++) + uint16_t seen_count = 0; + TIFFDirEntry *dp2; + uint16_t di2; + for (dp2 = dir, di2 = 0; di2 < dircount; dp2++, di2++) { - if (ma->tdir_tag == na->tdir_tag) + uint16_t tag = dp2->tdir_tag; + /* Binary search in seen[] (sorted). */ + int lo = 0, hi = (int)seen_count - 1, found = 0; + while (lo <= hi) { - na->tdir_ignore = TRUE; + int mid = (lo + hi) / 2; + if (seen[mid] == tag) { found = 1; break; } + else if (seen[mid] < tag) lo = mid + 1; + else hi = mid - 1; + } + if (found) + { + dp2->tdir_ignore = TRUE; + } + else + { + /* Insert tag into sorted seen[] (insertion sort step). */ + int pos = lo; /* insertion point */ + int k; + for (k = (int)seen_count; k > pos; k--) + seen[k] = seen[k - 1]; + seen[pos] = tag; + seen_count++; } } + _TIFFfreeExt(tif, seen); } + else + { + /* Fallback to O(D^2) if allocation fails (rare, small dircount). */ + TIFFDirEntry *ma; + uint16_t mb; + for (ma = dir, mb = 0; mb < dircount; ma++, mb++) + { + TIFFDirEntry *na; + uint16_t nb; + for (na = ma + 1, nb = mb + 1; nb < dircount; na++, nb++) + if (ma->tdir_tag == na->tdir_tag) + na->tdir_ignore = TRUE; + } + } } @@ -5394,18 +5397,44 @@ /* * Mark duplicates of any tag to be ignored (bugzilla 1994) * to avoid certain pathological problems. + * Fixed: same O(D^2) -> O(D log D) fix as in TIFFReadDirectory above. */ { - TIFFDirEntry *ma; - uint16_t mb; - for (ma = dir, mb = 0; mb < dircount; ma++, mb++) + uint16_t *seen = (uint16_t *)_TIFFmallocExt( + tif, (tmsize_t)(dircount * sizeof(uint16_t))); + if (seen != NULL) { - TIFFDirEntry *na; - uint16_t nb; - for (na = ma + 1, nb = mb + 1; nb < dircount; na++, nb++) + uint16_t seen_count = 0; + TIFFDirEntry *dp2; + uint16_t di2; + for (dp2 = dir, di2 = 0; di2 < dircount; dp2++, di2++) { - if (ma->tdir_tag == na->tdir_tag) + uint16_t tag = dp2->tdir_tag; + int lo = 0, hi = (int)seen_count - 1, found = 0; + while (lo <= hi) + { + int mid = (lo + hi) / 2; + if (seen[mid] == tag) { found = 1; break; } + else if (seen[mid] < tag) lo = mid + 1; + else hi = mid - 1; + } + if (found) { - na->tdir_ignore = TRUE; + dp2->tdir_ignore = TRUE; + } + else + { + int pos = lo; + int k; + for (k = (int)seen_count; k > pos; k--) + seen[k] = seen[k - 1]; + seen[pos] = tag; + seen_count++; } } + _TIFFfreeExt(tif, seen); } + else + { + TIFFDirEntry *ma; + uint16_t mb; + for (ma = dir, mb = 0; mb < dircount; ma++, mb++) + { + TIFFDirEntry *na; + uint16_t nb; + for (na = ma + 1, nb = mb + 1; nb < dircount; na++, nb++) + if (ma->tdir_tag == na->tdir_tag) + na->tdir_ignore = TRUE; + } + } }