-
Notifications
You must be signed in to change notification settings - Fork 13.7k
Open
Labels
A-dyn-traitArea: trait objects, vtable layoutArea: trait objects, vtable layoutC-bugCategory: This is a bug.Category: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-criticalCritical priorityCritical priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.T-typesRelevant to the types team, which will review and decide on the PR/issue.Relevant to the types team, which will review and decide on the PR/issue.
Description
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)
FeldrinH
Metadata
Metadata
Assignees
Labels
A-dyn-traitArea: trait objects, vtable layoutArea: trait objects, vtable layoutC-bugCategory: This is a bug.Category: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-criticalCritical priorityCritical priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.T-typesRelevant to the types team, which will review and decide on the PR/issue.Relevant to the types team, which will review and decide on the PR/issue.