Attached is the upstream fix (boostorg/context 0921b9fd5c) as a quilt
patch with DEP-3 headers, rebased to the package layout
(libs/context/include/...). It applies cleanly on top of
1.90.0-6ubuntu1 as the last entry in debian/patches/series.
Suggested changeling text:
* d/p/context-manage-exception-state-noinline.patch: re-apply
BOOST_NOINLINE to boost::context manage_exception_state so fibers
resumed on another thread no longer clobber that thread's exception
state (LP: #2173538)
Verification with the attached repro.cpp (resolute, arm64, g++ 15.2,
10 s per run, system header vs. patched header):
mode unpatched patched
rethrow "terminate called without an active 6.6M iterations, exit 0
exception", exit 134
check 1,626,229 of 8.8M catch blocks without 0 of 8.9M, exit 0
a current exception
** Patch added: "context-manage-exception-state-noinline.patch"
https://bugs.launchpad.net/ubuntu/+source/boost1.90/+bug/2173538/+attachment/6015559/+files/context-manage-exception-state-noinline.patch
** Description changed:
- 1) The release of Ubuntu you are using:
- Ubuntu 26.04.1 LTS (resolute), amd64 and arm64
+ [ Impact ]
- 2) The version of the package you are using:
- libboost1.90-dev / libboost-context1.90-dev 1.90.0-6ubuntu1, g++
15.2.0-16ubuntu1
+ Since Boost 1.90, boost::context fibers can overwrite the libstdc++
+ exception state (__cxa_get_globals) of another thread when a fiber
+ resumes on a different thread. manage_exception_state in
+ boost/context/fiber_fcontext.hpp lost its BOOST_NOINLINE, so gcc caches
+ the TLS address across the fiber switch.
- 3) What you expected to happen:
- An exception caught inside a boost::asio::spawn coroutine should stay the
current exception, so `throw;` rethrows it.
+ User-visible result: `throw;` inside a catch block in a
+ boost::asio::spawn coroutine aborts with "terminate called without an
+ active exception". With Icinga 2 on 26.04, about 1 in 240 cancelled
+ connections crashed an I/O thread; with the patched header, 0 in 12,675.
+ radosgw (ceph) is affected in the archive: it runs asio::spawn
+ coroutines on strands over a pool of rgw_thread_pool_size (default 512)
+ threads, and its amd64 binaries contain the broken code (see Other Info).
- 4) What happened instead:
- When coroutines move between threads, the exception state of another thread
gets overwritten. `throw;` inside a catch block then aborts with "terminate
called without an active exception":
+ This is a regression from noble: Boost 1.83 has no
+ manage_exception_state, so 24.04 is not affected.
- g++ -O2 -std=c++17 -pthread -o repro repro.cpp -lboost_context
- ./repro rethrow 10 # aborts within milliseconds; with the patch it runs 10
s and exits 0
+ The fix is the upstream commit 0921b9fd5c
+ (https://github.com/boostorg/context/pull/331), in Boost 1.92. It
+ restores BOOST_NOINLINE. The attached
+ context-manage-exception-state-noinline.patch is that commit as a quilt
+ patch for the package layout and applies cleanly on 1.90.0-6ubuntu1.
- This is https://github.com/boostorg/context/issues/323.
- `manage_exception_state` in boost/context/fiber_fcontext.hpp lost its
- `BOOST_NOINLINE`, so gcc caches the TLS address from
- `__cxa_get_globals()` across the fiber switch. It is fixed upstream in
- Boost 1.92 by commit 0921b9fd5c
- (https://github.com/boostorg/context/pull/331). The patch is attached;
- it applies unchanged to 1.90.0-6ubuntu1.
+ [ Test Plan ]
- We hit this with Icinga 2 (from packages.icinga.com) on 26.04: about 1
- in 240 cancelled connections crashed an I/O thread. After rebuilding the
- same sources against the patched header, we saw 0 in 12,675.
- radosgw/librgw2 from ceph show the same code pattern. Since the bug is
- in a header, affected packages need a rebuild after the fix.
+ 1. On resolute (amd64 and arm64), install libboost-context1.90-dev and
+ build the attached reproducer:
+ g++ -O2 -std=c++17 -pthread -o repro repro.cpp -lboost_context
+ 2. Run: ./repro rethrow 10
+ - Without the fix: aborts within milliseconds with
+ "terminate called without an active exception".
+ - With the fix: runs for 10 seconds and exits 0.
+ 3. Optionally run: ./repro check 10
+ - Without the fix: reports a large number of catch blocks without
+ a current exception and exits 1.
+ - With the fix: reports 0 and exits 0.
+ 4. Run the boost1.90 autopkgtests and check for regressions.
+
+ [ Where problems could occur ]
+
+ The patch only adds a noinline attribute to one header-only function,
+ so it does not change ABI. The worst case is a small slowdown on each
+ fiber switch, because the function is no longer inlined.
+
+ The fix sits in a header, so already-built packages stay affected until
+ they are rebuilt. In resolute, only ceph needs a no-change rebuild
+ (radosgw, librgw2, ceph-common). That rebuild could show unrelated
+ FTBFS or pick up other changes in ceph's build dependencies.
+
+ The other resolute packages that depend on libboost-context1.90.0 do
+ not need a rebuild:
+ - pdns-recursor, simgrid, yuzu: call jump_fcontext/make_fcontext
+ directly and never use fiber_fcontext.hpp.
+ - wesnoth-1.18 (server): asio::spawn, but io_context only runs on the
+ main thread.
+ - nix: coroutine2 sources/sinks are deliberately kept on the calling
+ thread (see the comment in src/libstore/filetransfer.cc).
+ - graph-tool: coroutine2 generators exposed to Python; only affected
+ if one is resumed from another thread while the first thread is
+ inside a C++ catch block, which the GIL makes impractical.
+ - libboost-fiber1.90.0 is built from boost1.90 itself and gets fixed
+ by this upload.
+ Third-party binaries built against the 1.90 headers (e.g. Icinga 2 from
+ packages.icinga.com) need to be rebuilt by their vendors.
+
+ [ Other Info ]
+
+ Upstream: https://github.com/boostorg/context/issues/323
+
+ Affected versions (resolute, amd64 and arm64):
+ libboost-context1.90-dev / libboost1.90-dev 1.90.0-6ubuntu1
+ g++ 15.2.0-16ubuntu1
+ Icinga 2 was installed from packages.icinga.com.
+
+ Affected binaries in ceph 20.2.0-0ubuntu2.1 (amd64): radosgw,
+ radosgw-admin, radosgw-object-expirer, librgw.so.2, denc-mod-rgw.so and
+ libceph_crypto_qat.so. In radosgw,
+ boost::asio::detail::spawned_fiber_thread::resume() keeps the address
+ returned by __cxa_get_globals() in a register across jump_fcontext and
+ writes the saved state back through it:
+
+ 7e91b1: call __cxa_get_globals@plt
+ 7e91c3: mov (%rax),%r14
+ 7e91c6: mov 0x8(%rax),%r13d
+ 7e91ca: mov %rax,%r12
+ 7e91cd: call boost::context::detail::jump_fcontext@plt
+ 7e91d2: mov %r14,(%r12) <- stale TLS address after the switch
+ 7e91da: mov %r13d,0x8(%r12)
+
+ spawned_fiber_thread::suspend_with() at 0x7f114d has the same sequence.
--
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2173538
Title:
boost1.90: fibers clobber the libstdc++ exception state of another
thread (upstream boostorg/context#323)
To manage notifications about this bug go to:
https://bugs.launchpad.net/boost/+bug/2173538/+subscriptions
--
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs