Program received signal SIGSEGV, Segmentation fault.
__strlen_evex () at ../sysdeps/x86_64/multiarch/strlen-evex.S:79
79 VPCMP $0, (%rdi), %YMMZERO, %k0
Missing separate debuginfos, use: dnf debuginfo-install bzip2-libs-1.0.8-12.fc37.x86_64 elfutils-libelf-0.188-3.fc37.x86_64 elfutils-libs-0.188-3.fc37.x86_64 libblkid-2.38.1-1.fc37.x86_64 libmount-2.38.1-1.fc37.x86_64 libunwind-1.6.2-5.fc37.x86_64 libzstd-1.5.2-3.fc37.x86_64 pcre2-10.40-1.fc37.1.x86_64 xz-libs-5.2.5-10.fc37.x86_64 zlib-1.2.12-5.fc37.x86_64
(gdb) backtrace full
#0 __strlen_evex () at ../sysdeps/x86_64/multiarch/strlen-evex.S:79
#1 0x00007ffff7713281 in core::ffi::c_str::CStr::from_ptr () at library/core/src/ffi/c_str.rs:286
#2 std::sys::unix::args::imp::clone::{closure#0} () at library/std/src/sys/unix/args.rs:146
#3 core::iter::adapters::map::map_fold::{closure#0}<isize, std::ffi::os_str::OsString, (), std::sys::unix::args::imp::clone::{closure_env#0}, core::iter::traits::iterator::Iterator::for_each::call::{closure_env#0}<std::ffi::os_str::OsString, alloc::vec::spec_extend::{impl#1}::spec_extend::{closure_env#0}<std::ffi::os_str::OsString, core::iter::adapters::map::Map<core::ops::range::Range<isize>, std::sys::unix::args::imp::clone::{closure_env#0}>, alloc::alloc::Global>>> () at library/core/src/iter/adapters/map.rs:84
#4 core::iter::traits::iterator::Iterator::fold<core::ops::range::Range<isize>, (), core::iter::adapters::map::map_fold::{closure_env#0}<isize, std::ffi::os_str::OsString, (), std::sys::unix::args::imp::clone::{closure_env#0}, core::iter::traits::iterator::Iterator::for_each::call::{closure_env#0}<std::ffi::os_str::OsString, alloc::vec::spec_extend::{impl#1}::spec_extend::{closure_env#0}<std::ffi::os_str::OsString, core::iter::adapters::map::Map<core::ops::range::Range<isize>, std::sys::unix::args::imp::clone::{closure_env#0}>, alloc::alloc::Global>>>>
() at library/core/src/iter/traits/iterator.rs:2414
#5 core::iter::adapters::map::{impl#2}::fold<std::ffi::os_str::OsString, core::ops::range::Range<isize>, std::sys::unix::args::imp::clone::{closure_env#0}, (), core::iter::traits::iterator::Iterator::for_each::call::{closure_env#0}<std::ffi::os_str::OsString, alloc::vec::spec_extend::{impl#1}::spec_extend::{closure_env#0}<std::ffi::os_str::OsString, core::iter::adapters::map::Map<core::ops::range::Range<isize>, std::sys::unix::args::imp::clone::{closure_env#0}>, alloc::alloc::Global>>> () at library/core/src/iter/adapters/map.rs:124
#6 core::iter::traits::iterator::Iterator::for_each<core::iter::adapters::map::Map<core::ops::range::Range<isize>, std::sys::unix::args::imp::clone::{closure_env#0}>, alloc::vec::spec_extend::{impl#1}::spec_extend::{closure_env#0}<std::ffi::os_str::OsString, core::iter::adapters::map::Map<core::ops::range::Range<isize>, std::sys::unix::args::imp::clone::{closure_env#0}>, alloc::alloc::Global>> () at library/core/src/iter/traits/iterator.rs:831
#7 alloc::vec::spec_extend::{impl#1}::spec_extend<std::ffi::os_str::OsString, core::iter::adapters::map::Map<core::ops::range::Range<isize>, std::sys::unix::args::imp::clone::{closure_env#0}>, alloc::alloc::Global> () at library/alloc/src/vec/spec_extend.rs:40
#8 alloc::vec::spec_from_iter_nested::{impl#1}::from_iter<std::ffi::os_str::OsString, core::iter::adapters::map::Map<core::ops::range::Range<isize>, std::sys::unix::args::imp::clone::{closure_env#0}>> () at library/alloc/src/vec/spec_from_iter_nested.rs:62
#9 alloc::vec::spec_from_iter::{impl#0}::from_iter<std::ffi::os_str::OsString, core::iter::adapters::map::Map<core::ops::range::Range<isize>, std::sys::unix::args::imp::clone::{closure_env#0}>> () at library/alloc/src/vec/spec_from_iter.rs:33
#10 alloc::vec::{impl#18}::from_iter<std::ffi::os_str::OsString, core::iter::adapters::map::Map<core::ops::range::Range<isize>, std::sys::unix::args::imp::clone::{closure_env#0}>> ()
at library/alloc/src/vec/mod.rs:2757
#11 core::iter::traits::iterator::Iterator::collect<core::iter::adapters::map::Map<core::ops::range::Range<isize>, std::sys::unix::args::imp::clone::{closure_env#0}>, alloc::vec::Vec<std::ffi::os_str::OsString, alloc::alloc::Global>> () at library/core/src/iter/traits/iterator.rs:1836
#12 std::sys::unix::args::imp::clone () at library/std/src/sys/unix/args.rs:144
#13 std::sys::unix::args::imp::args () at library/std/src/sys/unix/args.rs:129
#14 std::sys::unix::args::args () at library/std/src/sys/unix/args.rs:19
#15 std::env::args_os () at library/std/src/env.rs:792
#16 0x00007ffff7713151 in std::env::args () at library/std/src/env.rs:757
[...]
(gdb) info registers
rax 0x0 0
rbx 0x8 8
rcx 0x6b6e6973656b6166 7741240753840152934
rdx 0x8 8
rsi 0x6b6e6973656b6166 7741240753840152934
rdi 0x0 0
rbp 0x0 0x0
rsp 0x7fffffffc398 0x7fffffffc398
r8 0x7ffff7cacce0 140737350651104
r9 0x40 64
r10 0x0 0
r11 0x20 32
r12 0x55555556e890 93824992340112
r13 0x4 4
r14 0x5 5
r15 0x5555558748e0 93824995510496
rip 0x7ffff7c4093c 0x7ffff7c4093c <__strlen_evex+28>
Currently in
library/std/src/sys/unix/args.rs, Rust is hooking into the glibc.init_arrayextension for retrievingargc/argveven in case Rust does not managemain()and is e.g. just loaded into a process as acdylib.While this is great and convenient, it's unfortunately not implemented in a safe way. Various C commandline argument parsers are modifying
argvwhile parsing, so the information that gets passed into.init_arraymight not be correct anymore.Examples of such parsers are
getoptparser, noting " The default is to permute the contents of argv while scanning it so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. "GOptionContextparser, which removes all already handled arguments fromargvand only leaves others (removed ones are set toNULLand moved to the end).QCoreApplicationinQCoreApplicationPrivate::processCommandLineArguments(). It shuffles around arguments inargvand removes (by setting tonullptr) arguments it handled already.This means that at least
argccan easily be too big, and Rust would read beyond the (new) end ofargv. This caused crashes in practice because of callingstrlen()onNULLwhen creating anCStraround such an "removed" argument.Now the question is how this should be handled in Rust. I see three options here
.init_arrayextension usage and handle glibc like all other libcs.init_array. This means everybody has to pay for that even if they don't use arguments, and this is theoretically still not safe if the shared library is loaded at the same time theargvarray is modified and a partially written pointer value is readNULLpointers inargvby skipping over them. This means we would lose the exact length information of theargsiterator, and this is still theoretically not safe for the same reason as 2. See Stop at the firstNULLargument when iteratingargv#106001This code was added in 2019 by #66547 (CC @leo60228)
I'd be happy to provide a PR implementing either solution once there is some agreement how to move forward here.I've created #106001 as a proposed fix for this, which implements option 3.Meta
rustc --version --verbose:Backtrace
Also