Skip to content

SIGSEGV without unsafe when passing &dyn Trait to &dyn ParentTrait #145752

@chenxiaolong

Description

@chenxiaolong

As a workaround for &dyn A + B + C not being available yet, I've been defining my own empty traits with various combinations of parent traits to use for dynamic dispatch. However, in some scenarios, passing a &dyn Trait to &dyn <parent> for one of that trait's parent traits seems to trigger a SIGSEGV. This is a minimal reproducer I came up with that only uses std and has no unsafe blocks:

use std::io::{self, Cursor, Read, Seek, SeekFrom, Write}; pub trait ReadSeek: Read + Seek {} impl<R: Read + Seek> ReadSeek for R {} pub trait WriteSeek: Write + Seek {} impl<W: Write + Seek> WriteSeek for W {} pub trait ReadWriteSeek: ReadSeek + WriteSeek {} impl<W: ReadSeek + WriteSeek> ReadWriteSeek for W {} fn foo(writer: &mut dyn WriteSeek) -> io::Result<u64> { writer.seek(SeekFrom::Start(0)) } fn bar(file: &mut dyn ReadWriteSeek) -> io::Result<u64> { foo(file) } fn main() { let mut file = Cursor::new(Vec::new()); bar(&mut file).unwrap(); }

I'm on x86_64 Linux (Fedora 42), but this is reproducible in the Rust playground as well: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=ce176135d95ff18440c95782741706c4

(gdb and valgrind backtraces are included below.)

Meta

rustc --version --verbose:

rustc 1.89.0 (29483883e 2025-08-04) binary: rustc commit-hash: 29483883eed69d5fb4db01964cdf2af4d86e9cb2 commit-date: 2025-08-04 host: x86_64-unknown-linux-gnu release: 1.89.0 LLVM version: 20.1.7 

Also happens in the latest nightly build:

rustc 1.91.0-nightly (6ba0ce409 2025-08-21) binary: rustc commit-hash: 6ba0ce40941eee1ca02e9ba49c791ada5158747a commit-date: 2025-08-21 host: x86_64-unknown-linux-gnu release: 1.91.0-nightly LLVM version: 21.1.0 
Backtrace

gdb:

(gdb) bt #0 0x000055555555ac8d in core::io::borrowed_buf::BorrowedCursor::written (self=0x7fffffffd0e8) at /builddir/build/BUILD/rust-1.89.0-build/rustc-1.89.0-src/library/core/src/io/borrowed_buf.rs:227 #1 std::io::cursor::{impl#5}::read_buf_exact<alloc::vec::Vec<u8, alloc::alloc::Global>> (self=0x7fffffffd1d0, cursor=...) at /builddir/build/BUILD/rust-1.89.0-build/rustc-1.89.0-src/library/std/src/io/cursor.rs:373 #2 0x000055555555f030 in avbroot::foo (writer=...) at src/main.rs:16 #3 0x000055555555f053 in avbroot::bar (file=...) at src/main.rs:20 #4 0x000055555555f09b in avbroot::main () at src/main.rs:25 

valgrind:

==29216== Memcheck, a memory error detector ==29216== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al. ==29216== Using Valgrind-3.25.1 and LibVEX; rerun with -h for copyright info ==29216== Command: ./target/debug/segfault_test ==29216== ==29216== Invalid read of size 8 ==29216== at 0x40079FD: written (borrowed_buf.rs:227) ==29216== by 0x40079FD: <std::io::cursor::Cursor<T> as std::io::Read>::read_buf_exact (cursor.rs:373) ==29216== by 0x40093CF: segfault_test::foo (main.rs:16) ==29216== by 0x40093F2: segfault_test::bar (main.rs:20) ==29216== by 0x400943A: segfault_test::main (main.rs:25) ==29216== by 0x400A16A: core::ops::function::FnOnce::call_once (function.rs:250) ==29216== by 0x4005BCD: std::sys::backtrace::__rust_begin_short_backtrace (backtrace.rs:152) ==29216== by 0x40092D0: std::rt::lang_start::{{closure}} (rt.rs:206) ==29216== by 0x40239FF: call_once<(), (dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (function.rs:284) ==29216== by 0x40239FF: do_call<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panicking.rs:589) ==29216== by 0x40239FF: catch_unwind<i32, &(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (panicking.rs:552) ==29216== by 0x40239FF: catch_unwind<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panic.rs:359) ==29216== by 0x40239FF: {closure#0} (rt.rs:175) ==29216== by 0x40239FF: do_call<std::rt::lang_start_internal::{closure_env#0}, isize> (panicking.rs:589) ==29216== by 0x40239FF: catch_unwind<isize, std::rt::lang_start_internal::{closure_env#0}> (panicking.rs:552) ==29216== by 0x40239FF: catch_unwind<std::rt::lang_start_internal::{closure_env#0}, isize> (panic.rs:359) ==29216== by 0x40239FF: std::rt::lang_start_internal (rt.rs:171) ==29216== by 0x40092B6: std::rt::lang_start (rt.rs:205) ==29216== by 0x400957D: main (in /home/chenxiaolong/git/github/segfault_test/target/debug/segfault_test) ==29216== Address 0x10 is not stack'd, malloc'd or (recently) free'd ==29216== ==29216== Invalid read of size 8 ==29216== at 0x400B0CF: capacity (borrowed_buf.rs:76) ==29216== by 0x400B0CF: capacity (borrowed_buf.rs:218) ==29216== by 0x400B0CF: std::io::impls::<impl std::io::Read for &[u8]>::read_buf_exact (impls.rs:371) ==29216== by 0x4007A3B: <std::io::cursor::Cursor<T> as std::io::Read>::read_buf_exact (cursor.rs:375) ==29216== by 0x40093CF: segfault_test::foo (main.rs:16) ==29216== by 0x40093F2: segfault_test::bar (main.rs:20) ==29216== by 0x400943A: segfault_test::main (main.rs:25) ==29216== by 0x400A16A: core::ops::function::FnOnce::call_once (function.rs:250) ==29216== by 0x4005BCD: std::sys::backtrace::__rust_begin_short_backtrace (backtrace.rs:152) ==29216== by 0x40092D0: std::rt::lang_start::{{closure}} (rt.rs:206) ==29216== by 0x40239FF: call_once<(), (dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (function.rs:284) ==29216== by 0x40239FF: do_call<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panicking.rs:589) ==29216== by 0x40239FF: catch_unwind<i32, &(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (panicking.rs:552) ==29216== by 0x40239FF: catch_unwind<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panic.rs:359) ==29216== by 0x40239FF: {closure#0} (rt.rs:175) ==29216== by 0x40239FF: do_call<std::rt::lang_start_internal::{closure_env#0}, isize> (panicking.rs:589) ==29216== by 0x40239FF: catch_unwind<isize, std::rt::lang_start_internal::{closure_env#0}> (panicking.rs:552) ==29216== by 0x40239FF: catch_unwind<std::rt::lang_start_internal::{closure_env#0}, isize> (panic.rs:359) ==29216== by 0x40239FF: std::rt::lang_start_internal (rt.rs:171) ==29216== by 0x40092B6: std::rt::lang_start (rt.rs:205) ==29216== by 0x400957D: main (in /home/chenxiaolong/git/github/segfault_test/target/debug/segfault_test) ==29216== Address 0x8 is not stack'd, malloc'd or (recently) free'd ==29216== ==29216== ==29216== Process terminating with default action of signal 11 (SIGSEGV): dumping core ==29216== Access not within mapped region at address 0x8 ==29216== at 0x400B0CF: capacity (borrowed_buf.rs:76) ==29216== by 0x400B0CF: capacity (borrowed_buf.rs:218) ==29216== by 0x400B0CF: std::io::impls::<impl std::io::Read for &[u8]>::read_buf_exact (impls.rs:371) ==29216== by 0x4007A3B: <std::io::cursor::Cursor<T> as std::io::Read>::read_buf_exact (cursor.rs:375) ==29216== by 0x40093CF: segfault_test::foo (main.rs:16) ==29216== by 0x40093F2: segfault_test::bar (main.rs:20) ==29216== by 0x400943A: segfault_test::main (main.rs:25) ==29216== by 0x400A16A: core::ops::function::FnOnce::call_once (function.rs:250) ==29216== by 0x4005BCD: std::sys::backtrace::__rust_begin_short_backtrace (backtrace.rs:152) ==29216== by 0x40092D0: std::rt::lang_start::{{closure}} (rt.rs:206) ==29216== by 0x40239FF: call_once<(), (dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (function.rs:284) ==29216== by 0x40239FF: do_call<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panicking.rs:589) ==29216== by 0x40239FF: catch_unwind<i32, &(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (panicking.rs:552) ==29216== by 0x40239FF: catch_unwind<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panic.rs:359) ==29216== by 0x40239FF: {closure#0} (rt.rs:175) ==29216== by 0x40239FF: do_call<std::rt::lang_start_internal::{closure_env#0}, isize> (panicking.rs:589) ==29216== by 0x40239FF: catch_unwind<isize, std::rt::lang_start_internal::{closure_env#0}> (panicking.rs:552) ==29216== by 0x40239FF: catch_unwind<std::rt::lang_start_internal::{closure_env#0}, isize> (panic.rs:359) ==29216== by 0x40239FF: std::rt::lang_start_internal (rt.rs:171) ==29216== by 0x40092B6: std::rt::lang_start (rt.rs:205) ==29216== by 0x400957D: main (in /home/chenxiaolong/git/github/segfault_test/target/debug/segfault_test) ==29216== If you believe this happened as a result of a stack ==29216== overflow in your program's main thread (unlikely but ==29216== possible), you can try to increase the size of the ==29216== main thread stack using the --main-stacksize= flag. ==29216== The main thread stack size used in this run was 8388608. ==29216== ==29216== HEAP SUMMARY: ==29216== in use at exit: 460 bytes in 2 blocks ==29216== total heap usage: 9 allocs, 7 frees, 2,532 bytes allocated ==29216== ==29216== LEAK SUMMARY: ==29216== definitely lost: 0 bytes in 0 blocks ==29216== indirectly lost: 0 bytes in 0 blocks ==29216== possibly lost: 0 bytes in 0 blocks ==29216== still reachable: 460 bytes in 2 blocks ==29216== suppressed: 0 bytes in 0 blocks ==29216== Rerun with --leak-check=full to see details of leaked memory ==29216== ==29216== For lists of detected and suppressed errors, rerun with: -s ==29216== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0) 

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-dyn-traitArea: trait objects, vtable layoutC-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-criticalCritical priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions