Skip to content

Fix TraceStateBuilder.remove corrupting the builder when the same key is removed twice - #8613

Merged
jack-berg merged 1 commit into
open-telemetry:mainfrom
thswlsqls:fix/trace-state-builder-double-remove
Jul 21, 2026
Merged

Fix TraceStateBuilder.remove corrupting the builder when the same key is removed twice#8613
jack-berg merged 1 commit into
open-telemetry:mainfrom
thswlsqls:fix/trace-state-builder-double-remove

Conversation

@thswlsqls

Copy link
Copy Markdown
Contributor

Fixes #8612

Description

  • ArrayBasedTraceStateBuilder.remove() decremented numEntries without checking whether the value was already null, so removing a key twice drove the counter below the real count.
  • build() trusts numEntries in three places, so the drift leaks a {a=null} entry, silently drops live entries, or throws ArrayIndexOutOfBoundsException.
  • Mirrors the sibling put() (same class, line 74-78), which already does this check; first removal is unchanged.
  • No in-repo caller, but TraceStateBuilder is stable public API for external instrumentation and vendor propagators. The silent data loss contradicts the no-op contract of the remove(String) Javadoc ("if it is present") and of removeNotPresent(), and is reachable via reuseBuilder().
  • Same defect shape as Fix PooledHashMap dropping live entries when an entry is removed during forEach #8499 (PooledHashMap dropping live entries).

Testing done

  • Added TraceStateTest#removeTwice, #removeTwice_KeepsRemainingEntry, #removeTwice_KeepsRemainingEntries for one, two, and three entries. All three fail without the fix; 41 pass with it.
  • ./gradlew :api:all:check — passed, 504 tests.
  • No signature change (package-private class): apidiff unchanged.
  • CHANGELOG.md ## Unreleased### API entry added.

@thswlsqls
thswlsqls force-pushed the fix/trace-state-builder-double-remove branch from 184a512 to 0b8dc2f Compare July 16, 2026 21:00
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.64%. Comparing base (09d6c17) to head (d24559d).

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #8613      +/-   ##
============================================
- Coverage     91.64%   91.64%   -0.01%     
  Complexity    10348    10348              
============================================
  Files          1013     1013              
  Lines         27380    27381       +1     
  Branches       3218     3219       +1     
============================================
  Hits          25092    25092              
  Misses         1558     1558              
- Partials        730      731       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@thswlsqls
thswlsqls force-pushed the fix/trace-state-builder-double-remove branch from 0b8dc2f to d24559d Compare July 18, 2026 01:00
@thswlsqls
thswlsqls marked this pull request as ready for review July 18, 2026 01:03
@thswlsqls
thswlsqls requested a review from a team as a code owner July 18, 2026 01:03
@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Jul 18, 2026

Copy link
Copy Markdown

Pull request dashboard status

Merged · refreshed 2026-07-29 22:32 UTC

Status above doesn't look right?
  • Anything look wrong? Report it with what you expected; it helps us improve the dashboard.

@jack-berg
jack-berg merged commit 1ab326a into open-telemetry:main Jul 21, 2026
30 checks passed
@otelbot

otelbot Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Thank you for your contribution @thswlsqls! 🎉 We would like to hear from you about your experience contributing to OpenTelemetry by taking a few minutes to fill out this survey.

}

@Test
void removeTwice() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: rename to removeSameKeyTwice() - this could apply to all the added tests since all the added tests are removing the same key.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the review. You're right that all three added tests remove the same key, so removeSameKeyTwice* reads better. Since this was already merged, I'll fold the rename into a follow-up PR.

.put(FIRST_KEY, FIRST_VALUE)
.put(SECOND_KEY, SECOND_VALUE)
.put(THIRD_KEY, THIRD_VALUE)
.remove(FIRST_KEY)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: just to add variety, remove the SECOND_KEY twice here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point — right now every double-remove test matches on the first entry, so removing SECOND_KEY here would exercise a later slot in the scan loop. I'll include it in the same follow-up.

@thswlsqls
thswlsqls deleted the fix/trace-state-builder-double-remove branch July 29, 2026 03:23
thswlsqls added a commit to thswlsqls/opentelemetry-java that referenced this pull request Jul 30, 2026
Follow-up to review comments on open-telemetry#8613, which were submitted after that PR
had already been merged.

Rename removeTwice, removeTwice_KeepsRemainingEntry and
removeTwice_KeepsRemainingEntries to removeSameKeyTwice*, since all three
remove the same key and the previous names did not say so.

removeSameKeyTwice_KeepsRemainingEntries now removes SECOND_KEY rather than
FIRST_KEY. ArrayBasedTraceStateBuilder.remove scans entries from the front,
so removing the first key always matches on the first iteration; removing a
middle key covers a later slot for the first time.

Test-only: no production code, public API or asserted behavior changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TraceStateBuilder.remove corrupts the builder when the same key is removed twice

3 participants