Skip to content

Fix double destructor call in channel iterators#339

Open
a2Fsa2k wants to merge 1 commit into
boostorg:developfrom
a2Fsa2k:fix/channel-iterator-double-free
Open

Fix double destructor call in channel iterators#339
a2Fsa2k wants to merge 1 commit into
boostorg:developfrom
a2Fsa2k:fix/channel-iterator-double-free

Conversation

@a2Fsa2k
Copy link
Copy Markdown

@a2Fsa2k a2Fsa2k commented May 27, 2026

Summary

  • Fixes a double destructor call in buffered_channel and unbuffered_channel iterators that causes undefined behavior (double-free) with non-trivially-destructible types.
  • Removes a redundant destructor call in operator++() that was already handled inside increment_().
  • Adds regression tests using a type with an atomic destruction counter.

Problem

When iterating over a buffered_channel<T> or unbuffered_channel<T> with range-for where T has a non-trivial destructor (e.g., std::string, shared_ptr), the destructor is called twice on every increment. This is a double-free — undefined behavior that manifests as crashes or corruption.

Root cause

Two overlapping commits created the double-destroy:

Since operator++() calls increment_(), the destructor runs twice on the same dead object.

Fix

Remove the explicit destructor call in operator++() from both buffered_channel.hpp:412 and unbuffered_channel.hpp:447. The call in increment_() handles it correctly.

Testing

  • Existing tests pass (they used int, which masked the bug — trivially destructible).
  • New test_rangefor_non_trivial_dtor tests in both channel test files use counted_type, a type with an atomic destruction counter, verifying each object is destroyed exactly once.
  • Both headers compile clean with -fsyntax-only.

Files changed

File Lines Change
include/boost/fiber/buffered_channel.hpp -1 Remove redundant ~value_type()
include/boost/fiber/unbuffered_channel.hpp -1 Remove redundant ~value_type()
test/test_buffered_channel_post.cpp +75 Regression test
test/test_unbuffered_channel_post.cpp +75 Regression test

… iterators

Commit 37b5f77 (boostorg#209) added an explicit destructor call in operator++() for
channel iterators. Later, commit e440623 (boostorg#258) added the same destructor
call inside increment_() which operator++() calls. This results in the
destructor running twice on every iterator increment, causing undefined
behavior (double-free) for non-trivially-destructible types.

This bug affects all users of range-for loops over buffered_channel and
unbuffered_channel with types like std::string, shared_ptr, etc.

Fix by removing the redundant destructor call in operator++() since
increment_() already handles it correctly.

Regression test uses a type with an atomic destruction counter to verify
objects are destroyed exactly once.
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.

1 participant