rustc emits a vacant vtable slot for a callable boxed async service (Rust 1.98)
Pangram verdict · v3.3
We believe that this text is a mix of AI and human-written content.
AI likelihood · overall
MixedArticle text · 460 words · 5 segments analyzed
View all comments Observed on aarch64-apple-darwin. It also consistently hits my CI runners since release of 1.98, failing our CI... each commit (https://github.com/plabayo/rama) The same Rama source works with Rust 1.97.1, crashes with Rust 1.98.0, and works again with nightly-2026-07-16. Rust 1.98 emits a zero method entry in a compiler-generated vtable. Safe Rust dispatches through that entry and the process segfaults at address zero.
Reproduction Based on my CI failures I made a small example using our rama Repo, note that it assumes TCP port 62017 is available: git clone https://github.com/plabayo/rama.git cd rama git checkout --detach 0b4aa58e33e9d07db2718a3886c05b673ba9a70e rustup toolchain install 1.98.0 just clean CARGO_TARGET_DIR=target-ticket-198 \ cargo +1.98.0 build \ -p rama-examples \ --features=cli,tcp,http-full,proxy-full,boring \ --bin http_mitm_proxy_boring Than run: ./target-ticket-198/debug/http_mitm_proxy_boring \ >/tmp/rama-server-198.log 2>&1 & proxy_pid=$!
sleep 2 curl -skv --max-time 5 \ --proxy http://john:secret@127.0.0.1:62017 \ https://127.0.0.1:1/ \ >/tmp/rama-curl-198.log 2>&1 || true if kill -0 "$proxy_pid" 2>/dev/null; then echo "proxy is still alive" kill "$proxy_pid" wait "$proxy_pid" 2>/dev/null || true else wait "$proxy_pid" echo "proxy exited with status $?" fi sed -n '1,120p' /tmp/rama-curl-198.log sed -n '1,120p' /tmp/rama-server-198.log Repeat with separate targets (w/ clean target directory) for 1.97.1 and nightly-2026-07-16 (tried to narrow down...). What you should see is the following: 1.97.1: 502 Bad Gateway; proxy remains alive 1.98.0: CONNECT aborts; SIGSEGV at address zero nightly-2026-07-16: 502 Bad Gateway; proxy remains alive In short, it is expected that the failed upstream connection should be converted into HTTP/1.1 502 Bad Gateway, and the proxy should remain alive. Instead it is obsered in 1.98 that the CONNECT request is aborted. macOS reports SIGSEGV with pc=0. The generated caller loads the Service method from vtable + 0x18 and branches to the zero value stored there. Diving into the code it seems that the Rust 1.98 vtable contains valid drop/size/alignment fields, but the first method slot at offset 24 is zero. -Zprint-mono-items collects the erased caller on Rust 1.98, but not EagerHttpProxyConnector's concrete Service::serve method. nightly-2026-07-16 collects both the method and its async closures. The method may be reaching VtblEntry::Vacant because its predicates are incorrectly considered impossible. Relevant rama types: DynService/BoxService EagerHttpProxyConnector::serve responder.boxed() boundary Meta macOS crash report: Exception: EXC_BAD_ACCESS (SIGSEGV) Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000 Termination: Segmentation fault: 11 Faulting thread: tokio-rt-worker PC: 0x0000000000000000 LR: 0x0000000102511674 The link register resolves to: rama_http::layer::upgrade::service:: <UpgradeService<...> as Service<Request>>::serve::{closure#0} + 2956 Backtrace Thread 2 Crashed: tokio-rt-worker 0 0x0000000000000000 1 <UpgradeService<...> as Service<Request>>::serve::{closure#0} 2 <tracing::instrument::Instrumented< Trace<...>::serve::{closure#0} > as Future>::poll 3 <rama_http_core::proto::h1::dispatch::Server<...> as Dispatch>::recv_msg::{closure#0} 4 rama_http_core::proto::h1::dispatch::Dispatcher<...>::poll_write 5 rama_http_core::proto::h1::dispatch::Dispatcher<...>::poll_catch 6 <rama_http_core::server::conn::http1:: UpgradeableConnection<...> as Future>::poll 7 <rama_http_core::server::conn::auto:: UpgradeableConnection<...> as Future>::poll ...
13 tokio::runtime::scheduler::multi_thread::worker::Context::run_task 14 tokio::runtime::scheduler::multi_thread::worker::Context::run The program counter is exactly zero.
The link register points inside UpgradeService::serve, at the indirect boxed Service call. Together with the emitted vtable containing zero at method offset 0x18, this indicates that dispatch branches through the null vtable entry.