Skip to content

Fix Debug assert in IntervalTree's is_valid() with til::point keys - #20487

Open
steffen-heil-secforge wants to merge 2 commits into
microsoft:mainfrom
secforge:fix/interval-tree-is-valid-numeric-limits
Open

Fix Debug assert in IntervalTree's is_valid() with til::point keys#20487
steffen-heil-secforge wants to merge 2 commits into
microsoft:mainfrom
secforge:fix/interval-tree-is-valid-numeric-limits

Conversation

@steffen-heil-secforge

@steffen-heil-secforge steffen-heil-secforge commented Jul 30, 2026

Copy link
Copy Markdown

assert(is_valid().first) in oss/interval_tree/IntervalTree.h fires in Debug builds whenever 64 or more URLs get autodetected on screen. for /l %i in (1,1,80) do @echo https://example.com/%i is enough to do it.

is_valid() seeds its bounds accumulators with std::numeric_limits<Scalar>::max()/::min(). We instantiate the tree with Scalar = til::point, which has no specialization, so the primary template hands back a default-constructed til::point{0,0} for both sentinels. The right-subtree check then asks {0,0} <= center, which is true for any real tree, so validation fails. 64 is the magic number because that's where the tree starts splitting into subtrees at all.

The fix is to stop needing sentinels — track emptiness explicitly instead. I also switched the max-stop accumulator from std::min to std::max, which is a separate upstream bug and the reason nobody upstream ever hit this: std::min pins that accumulator at numeric_limits<Scalar>::min() forever, so the left-subtree check (valid.second.second >= center) can never fire. You can see it by printing what is_valid() returns — for a tree whose real maximum stop is {10,99}, upstream's accumulator comes back as {INT32_MIN,INT32_MIN}. The right-subtree check reads the min-start accumulator and is unaffected.

That's also the trade-off against just specializing std::numeric_limits<til::point> and leaving this file alone (see @lhecker's comment below): that does fix the crash, but the max-stop accumulator stays dead, so the left-subtree constraint is never actually checked. Happy to go that route instead if you prefer the smaller footprint — it's your call.

Release builds were unaffected and the tree itself was always structurally valid — only the checker was wrong.

On editing a vendored file: MAINTAINER_README.md says take it wholesale and change nothing, so this needs an excuse. The file already isn't upstream's, though — every existing difference comes from ekg/intervaltree#31, opened by @PankajBhojwani in 2020 and vendored here 27 days later, still unmerged. Without it this doesn't compile against til::point at all, and it's also what made this bug reachable: it taught the constructor about struct keys but not is_valid(). Upstream's last commit was March 2021, so there's nothing to wait for. Both deviations are now written down in the README, along with a warning that re-importing wholesale will break the build.

Two things I left alone but can fold in: cgmanifest.json pins a hash whose content doesn't match the file, and I haven't reported the std::min bug upstream.

Built and tested locally, Debug|x64: the new IntervalTreeTests pass (2/2), and the full til suite is 280/280 with no regressions. Reverting just the header makes the new test hit the assert, though it does so by blocking on the assert dialog rather than failing cleanly.

Closes #20486

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Comment thread src/til/ut_til/IntervalTreeTests.cpp Fixed
@github-actions

This comment has been minimized.

is_valid() seeded its bounds accumulators with std::numeric_limits<Scalar>
::max()/min(). We instantiate the tree with Scalar = til::point, which has
no std::numeric_limits specialization, so the primary template returned a
default-constructed til::point{0,0} for *both* sentinels. The right-subtree
constraint check then evaluated {0,0} <= center, which holds for essentially
any real tree, so is_valid() returned false and assert(is_valid().first)
fired.

The tree only splits into subtrees once it holds 64 intervals, its default
minimum bucket size, so the threshold was exactly 64: Terminal::_getPatterns
builds the pattern tree from autodetected URLs over roughly three viewport-
heights of buffer, making 64 matches easy to reach. Any Debug build showing
a screenful of links aborted. Release builds were unaffected, and the tree
itself was structurally sound -- only the checker was wrong.

Track emptiness explicitly so no sentinel value is needed. Also accumulate
the maximum stop with std::max rather than std::min; upstream's std::min
pinned that accumulator at numeric_limits<Scalar>::min() forever, silently
reducing both center checks to no-ops, so a numeric_limits<til::point>
specialization alone would have left the check dead.

This is the second local deviation from ekg/intervaltree, alongside the
struct-key support carried since microsoft#7691 (upstream PR ekg/intervaltree#31,
still unmerged). Both are now recorded in MAINTAINER_README.md, together
with a warning that re-importing the file wholesale breaks the build.

Closes microsoft#20486

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pr3nsYBYoJ66jgRidRHyJF
@steffen-heil-secforge
steffen-heil-secforge force-pushed the fix/interval-tree-is-valid-numeric-limits branch from eb587fd to 146c788 Compare July 30, 2026 17:44
@lhecker

lhecker commented Jul 30, 2026

Copy link
Copy Markdown
Member

Can you edit the PR description to have a bit more of a human touch? It's longer than the diff lol.

@steffen-heil-secforge

Copy link
Copy Markdown
Author

Ha, fair. Trimmed it by half and cut the bullet-point wall — the vendored-file justification and the "I couldn't build this on Windows" caveat are the two bits I kept, since I figured you'd want both up front.

Comment thread oss/interval_tree/IntervalTree.h Outdated

@lhecker lhecker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, I'd prefer a specialization of til::point, til::size and so on. Regardless, this is an excellent find. Thank you for it!

@microsoft-github-policy-service microsoft-github-policy-service Bot added the Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something label Jul 30, 2026
…rval_tree

Per review feedback, revert the change to oss/interval_tree/IntervalTree.h
and fix the root assumption on our side instead: is_valid() requires
std::numeric_limits<Scalar> to be meaningful, so specialize it for the til
coordinate types rather than teaching the vendored file to do without.

The vendored header is now byte-identical to what it was before this branch.

Also add <limits> to LibraryIncludes.h. It was not included anywhere, and
relying on a transitive include to declare the primary template before
specializing it is fragile.

Note this fixes the assert without making is_valid() correct: upstream
accumulates the maximum stop with std::min, so that accumulator stays pinned
at numeric_limits<Scalar>::min() and the left-subtree check can never fire.
The right-subtree check reads the min-start accumulator and works. That
trade-off is accepted deliberately -- see the PR discussion.

Verified Debug|x64: IntervalTreeTests 3/3, full til suite 281/281.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pr3nsYBYoJ66jgRidRHyJF
@steffen-heil-secforge

Copy link
Copy Markdown
Author

Done — specialized numeric_limits for til::point and til::size, reverted the interval_tree change, and added <limits> to LibraryIncludes since it wasn't pulled in reliably. Kept a short note in the README about the pre-existing ekg#31 deviation. Let me know if you want other til types covered. Verified Debug|x64 locally: til suite 281/281.

And sorry for having Claude write the texts. I do review them (code and messages), but his English is better than mine, and more precise.

@microsoft-github-policy-service microsoft-github-policy-service Bot added Needs-Attention The core contributors need to come back around and look at this ASAP. and removed Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something labels Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs-Attention The core contributors need to come back around and look at this ASAP.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Debug assert in IntervalTree.h:209 (is_valid) whenever 64+ URLs are autodetected

3 participants