Skip to content

Stop AnsiSkippingString treating literal 0xff bytes as its own sentinel - #3199

Open
afonsojanu wants to merge 1 commit into
catchorg:develfrom
afonsojanu:fix/ansi-skipping-string-literal-0xff-byte
Open

Stop AnsiSkippingString treating literal 0xff bytes as its own sentinel#3199
afonsojanu wants to merge 1 commit into
catchorg:develfrom
afonsojanu:fix/ansi-skipping-string-literal-0xff-byte

Conversation

@afonsojanu

Copy link
Copy Markdown

Closes #2960

AnsiSkippingString marks the terminating m of a recognized ansi escape sequence by overwriting it with 0xff internally, and flips it back when a substring is produced. The trouble is a literal 0xff byte that was already part of the original text (for instance from stringifying raw byte data, as in the linked issue) looks exactly the same as one of these markers, and the existing code has no way to tell them apart.

Two consequences of that, both reachable with plain text that has nothing to do with ansi escapes at all:

  • substring() blindly turns every 0xff byte in the output back into 'm', silently corrupting real data.
  • the reverse iterator, on hitting a 0xff byte, assumes it must have found one of its own markers and starts scanning backwards for the \033 that "must" precede it. If the byte was never one of ours, that scan walks straight off the beginning of the string, tripping an assert in debug builds (and undefined behavior in release, matching the exception/crash reports in the issue).

The fix records the byte offsets where a real sentinel was inserted (m_sentinelPositions) and checks membership there instead of comparing raw byte values, in both substring() and the iterator's unadvance()/tryParseAnsiEscapes(). A plain 0xff byte in the text is now left completely alone.

Added a test (TextFlow::Column handles literal 0xff bytes that are not ansi escapes) that:

  • reproduces the crash on an unmodified build (confirmed it aborts with the exact assert from the issue before this change)
  • passes cleanly with the fix
  • also checks that genuine ansi-escape wrapping still round-trips correctly, so the existing sentinel behavior isn't disturbed

Ran the full self-test suite before and after; identical pass/fail counts either way (the one pre-existing [!shouldfail] TextFlow test is unrelated to this).

AnsiSkippingString marks the end of a recognized ansi escape sequence
by overwriting its terminating 'm' with 0xff internally, then flips it
back when producing a substring. The problem is that any literal 0xff
byte already present in the original text (say, from stringifying raw
bytes) looks exactly the same, so the code can't tell the two apart.
substring() ends up turning real data bytes into 'm', and the reverse
iterator can walk clean off the start of the string hunting for an
escape sequence that was never there, hitting an assert in debug
builds or worse in release.

This tracks the byte offsets where a genuine sentinel was inserted
and checks against those instead of comparing raw byte values, so a
plain 0xff in the text is left alone in both places. Added a test that
reproduces the crash on an unmodified build and passes with the fix.

Closes catchorg#2960
@codecov

codecov Bot commented Sep 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.19%. Comparing base (897d804) to head (317fbd8).

Additional details and impacted files
@@            Coverage Diff             @@
##            devel    #3199      +/-   ##
==========================================
+ Coverage   91.17%   91.19%   +0.02%     
==========================================
  Files         206      206              
  Lines        9031     9048      +17     
==========================================
+ Hits         8234     8251      +17     
  Misses        797      797              
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@horenmar

horenmar commented Sep 6, 2026

Copy link
Copy Markdown
Member

In a funny coincidence, I did some work on TextFlow today and noticed the same issue. As I went in a different direction, I will merge my changes first, and then take a look at what remains from this PR later this week.

@horenmar horenmar added the BugFix label Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Issue with printing std::vector content

2 participants