From ddee45e1d7fd34563c13513d974f792fae41a2f7 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Tue, 5 Jul 2022 19:56:22 +0200 Subject: [PATCH 1/5] Support `#[unix_sigpipe = "inherit|sig_dfl|sig_ign"]` on `fn main()` This makes it possible to instruct libstd to never touch the signal handler for `SIGPIPE`, which makes programs pipeable by default (e.g. with `./your-program | head -n 1`) without `ErrorKind::BrokenPipe` errors. --- .../rustc_codegen_cranelift/src/main_shim.rs | 15 +++-- compiler/rustc_codegen_ssa/src/base.rs | 16 ++++-- compiler/rustc_feature/src/active.rs | 2 + compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_monomorphize/src/collector.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_passes/src/entry.rs | 56 ++++++++++++++----- compiler/rustc_session/src/config.rs | 12 +++- compiler/rustc_session/src/config/sigpipe.rs | 20 +++++++ compiler/rustc_span/src/symbol.rs | 4 ++ compiler/rustc_typeck/src/lib.rs | 2 +- library/std/src/rt.rs | 12 +++- library/std/src/sys/hermit/mod.rs | 2 +- library/std/src/sys/sgx/mod.rs | 2 +- library/std/src/sys/solid/mod.rs | 2 +- library/std/src/sys/unix/mod.rs | 29 ++++++++-- library/std/src/sys/unsupported/common.rs | 2 +- library/std/src/sys/windows/mod.rs | 2 +- .../src/language-features/unix-sigpipe.md | 54 ++++++++++++++++++ .../unix_sigpipe/auxiliary/sigpipe-utils.rs | 26 +++++++++ .../unix_sigpipe/unix_sigpipe-crate.rs | 4 ++ .../unix_sigpipe/unix_sigpipe-crate.stderr | 13 +++++ .../unix_sigpipe/unix_sigpipe-duplicates.rs | 5 ++ .../unix_sigpipe-duplicates.stderr | 14 +++++ .../unix_sigpipe/unix_sigpipe-error.rs | 13 +++++ .../unix_sigpipe/unix_sigpipe-inherit.rs | 14 +++++ .../unix_sigpipe/unix_sigpipe-list.rs | 4 ++ .../unix_sigpipe/unix_sigpipe-list.stderr | 15 +++++ .../unix_sigpipe/unix_sigpipe-non-main-fn.rs | 6 ++ .../unix_sigpipe-non-main-fn.stderr | 8 +++ .../unix_sigpipe-non-root-main.rs | 8 +++ .../unix_sigpipe-non-root-main.stderr | 8 +++ .../unix_sigpipe/unix_sigpipe-not-used.rs | 9 +++ .../unix_sigpipe/unix_sigpipe-only-feature.rs | 13 +++++ .../unix_sigpipe/unix_sigpipe-rustc_main.rs | 15 +++++ .../unix_sigpipe/unix_sigpipe-sig_dfl.rs | 13 +++++ .../unix_sigpipe/unix_sigpipe-start.rs | 6 ++ .../unix_sigpipe/unix_sigpipe-start.stderr | 8 +++ .../unix_sigpipe/unix_sigpipe-struct.rs | 6 ++ .../unix_sigpipe/unix_sigpipe-struct.stderr | 8 +++ .../unix_sigpipe/unix_sigpipe-wrong.rs | 4 ++ .../unix_sigpipe/unix_sigpipe-wrong.stderr | 8 +++ .../attributes/unix_sigpipe/unix_sigpipe.rs | 4 ++ .../unix_sigpipe/unix_sigpipe.stderr | 8 +++ .../feature-gate-unix_sigpipe.rs | 4 ++ .../feature-gate-unix_sigpipe.stderr | 12 ++++ 46 files changed, 449 insertions(+), 43 deletions(-) create mode 100644 compiler/rustc_session/src/config/sigpipe.rs create mode 100644 src/doc/unstable-book/src/language-features/unix-sigpipe.md create mode 100644 src/test/ui/attributes/unix_sigpipe/auxiliary/sigpipe-utils.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-crate.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-crate.stderr create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-duplicates.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-duplicates.stderr create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-error.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-inherit.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-list.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-list.stderr create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-main-fn.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-main-fn.stderr create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-root-main.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-root-main.stderr create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-not-used.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-only-feature.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-rustc_main.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-sig_dfl.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-start.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-start.stderr create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-struct.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-struct.stderr create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-wrong.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe-wrong.stderr create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe.rs create mode 100644 src/test/ui/attributes/unix_sigpipe/unix_sigpipe.stderr create mode 100644 src/test/ui/feature-gates/feature-gate-unix_sigpipe.rs create mode 100644 src/test/ui/feature-gates/feature-gate-unix_sigpipe.stderr diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index c67b6e98b32..3c024a84d90 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -1,7 +1,7 @@ use rustc_hir::LangItem; use rustc_middle::ty::subst::GenericArg; use rustc_middle::ty::AssocKind; -use rustc_session::config::EntryFnType; +use rustc_session::config::{sigpipe, EntryFnType}; use rustc_span::symbol::Ident; use crate::prelude::*; @@ -15,12 +15,12 @@ pub(crate) fn maybe_create_entry_wrapper( is_jit: bool, is_primary_cgu: bool, ) { - let (main_def_id, is_main_fn) = match tcx.entry_fn(()) { + let (main_def_id, (is_main_fn, sigpipe)) = match tcx.entry_fn(()) { Some((def_id, entry_ty)) => ( def_id, match entry_ty { - EntryFnType::Main => true, - EntryFnType::Start => false, + EntryFnType::Main { sigpipe } => (true, sigpipe), + EntryFnType::Start => (false, sigpipe::DEFAULT), }, ), None => return, @@ -35,7 +35,7 @@ pub(crate) fn maybe_create_entry_wrapper( return; } - create_entry_fn(tcx, module, unwind_context, main_def_id, is_jit, is_main_fn); + create_entry_fn(tcx, module, unwind_context, main_def_id, is_jit, is_main_fn, sigpipe); fn create_entry_fn( tcx: TyCtxt<'_>, @@ -44,6 +44,7 @@ pub(crate) fn maybe_create_entry_wrapper( rust_main_def_id: DefId, ignore_lang_start_wrapper: bool, is_main_fn: bool, + sigpipe: u8, ) { let main_ret_ty = tcx.fn_sig(rust_main_def_id).output(); // Given that `main()` has no arguments, @@ -83,6 +84,7 @@ pub(crate) fn maybe_create_entry_wrapper( bcx.switch_to_block(block); let arg_argc = bcx.append_block_param(block, m.target_config().pointer_type()); let arg_argv = bcx.append_block_param(block, m.target_config().pointer_type()); + let arg_sigpipe = bcx.ins().iconst(types::I8, sigpipe as i64); let main_func_ref = m.declare_func_in_func(main_func_id, &mut bcx.func); @@ -143,7 +145,8 @@ pub(crate) fn maybe_create_entry_wrapper( let main_val = bcx.ins().func_addr(m.target_config().pointer_type(), main_func_ref); let func_ref = m.declare_func_in_func(start_func_id, &mut bcx.func); - let call_inst = bcx.ins().call(func_ref, &[main_val, arg_argc, arg_argv]); + let call_inst = + bcx.ins().call(func_ref, &[main_val, arg_argc, arg_argv, arg_sigpipe]); bcx.inst_results(call_inst)[0] } else { // using user-defined start fn diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 4c6be3f9108..6e482062383 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -389,15 +389,14 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let main_llfn = cx.get_fn_addr(instance); - let use_start_lang_item = EntryFnType::Start != entry_type; - let entry_fn = create_entry_fn::(cx, main_llfn, main_def_id, use_start_lang_item); + let entry_fn = create_entry_fn::(cx, main_llfn, main_def_id, entry_type); return Some(entry_fn); fn create_entry_fn<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx: &'a Bx::CodegenCx, rust_main: Bx::Value, rust_main_def_id: DefId, - use_start_lang_item: bool, + entry_type: EntryFnType, ) -> Bx::Function { // The entry function is either `int main(void)` or `int main(int argc, char **argv)`, // depending on whether the target needs `argc` and `argv` to be passed in. @@ -442,7 +441,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let i8pp_ty = cx.type_ptr_to(cx.type_i8p()); let (arg_argc, arg_argv) = get_argc_argv(cx, &mut bx); - let (start_fn, start_ty, args) = if use_start_lang_item { + let (start_fn, start_ty, args) = if let EntryFnType::Main { sigpipe } = entry_type { let start_def_id = cx.tcx().require_lang_item(LangItem::Start, None); let start_fn = cx.get_fn_addr( ty::Instance::resolve( @@ -454,8 +453,13 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( .unwrap() .unwrap(), ); - let start_ty = cx.type_func(&[cx.val_ty(rust_main), isize_ty, i8pp_ty], isize_ty); - (start_fn, start_ty, vec![rust_main, arg_argc, arg_argv]) + + let i8_ty = cx.type_i8(); + let arg_sigpipe = bx.const_u8(sigpipe); + + let start_ty = + cx.type_func(&[cx.val_ty(rust_main), isize_ty, i8pp_ty, i8_ty], isize_ty); + (start_fn, start_ty, vec![rust_main, arg_argc, arg_argv, arg_sigpipe]) } else { debug!("using user-defined start fn"); let start_ty = cx.type_func(&[isize_ty, i8pp_ty], isize_ty); diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 22178dd2123..89103a87623 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -519,6 +519,8 @@ declare_features! ( /// Allows creation of instances of a struct by moving fields that have /// not changed from prior instances of the same struct (RFC #2528) (active, type_changing_struct_update, "1.58.0", Some(86555), None), + /// Enables rustc to generate code that instructs libstd to NOT ignore SIGPIPE. + (active, unix_sigpipe, "CURRENT_RUSTC_VERSION", Some(97889), None), /// Allows unsized fn parameters. (active, unsized_fn_params, "1.49.0", Some(48055), None), /// Allows unsized rvalues at arguments and parameters. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index d520efed9b8..00621341ee3 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -359,6 +359,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // Entry point: + gated!(unix_sigpipe, Normal, template!(Word, NameValueStr: "inherit|sig_ign|sig_dfl"), ErrorFollowing, experimental!(unix_sigpipe)), ungated!(start, Normal, template!(Word), WarnFollowing), ungated!(no_start, CrateLevel, template!(Word), WarnFollowing), ungated!(no_main, CrateLevel, template!(Word), WarnFollowing), diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 82ef16a7f72..fceca58530c 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1315,7 +1315,7 @@ impl<'v> RootCollector<'_, 'v> { /// the return type of `main`. This is not needed when /// the user writes their own `start` manually. fn push_extra_entry_roots(&mut self) { - let Some((main_def_id, EntryFnType::Main)) = self.entry_fn else { + let Some((main_def_id, EntryFnType::Main { .. })) = self.entry_fn else { return; }; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 5789531d2ff..94a1e63e3f9 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2117,6 +2117,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { sym::automatically_derived, sym::start, sym::rustc_main, + sym::unix_sigpipe, sym::derive, sym::test, sym::test_case, diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index 7381019a620..cd10170d3ba 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -1,11 +1,11 @@ -use rustc_ast::{entry::EntryPointType, Attribute}; +use rustc_ast::entry::EntryPointType; use rustc_errors::struct_span_err; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::{ItemId, Node, CRATE_HIR_ID}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{DefIdTree, TyCtxt}; -use rustc_session::config::{CrateType, EntryFnType}; +use rustc_session::config::{sigpipe, CrateType, EntryFnType}; use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol, DUMMY_SP}; @@ -71,14 +71,12 @@ fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> Entry } } -fn err_if_attr_found(ctxt: &EntryContext<'_>, attrs: &[Attribute], sym: Symbol) { +fn err_if_attr_found(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol, details: &str) { + let attrs = ctxt.tcx.hir().attrs(id.hir_id()); if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym) { ctxt.tcx .sess - .struct_span_err( - attr.span, - &format!("`{}` attribute can only be used on functions", sym), - ) + .struct_span_err(attr.span, &format!("`{}` attribute {}", sym, details)) .emit(); } } @@ -87,14 +85,16 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { let at_root = ctxt.tcx.opt_local_parent(id.def_id) == Some(CRATE_DEF_ID); match entry_point_type(ctxt, id, at_root) { - EntryPointType::None => (), + EntryPointType::None => { + err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on `fn main()`"); + } _ if !matches!(ctxt.tcx.def_kind(id.def_id), DefKind::Fn) => { - let attrs = ctxt.tcx.hir().attrs(id.hir_id()); - err_if_attr_found(ctxt, attrs, sym::start); - err_if_attr_found(ctxt, attrs, sym::rustc_main); + err_if_attr_found(ctxt, id, sym::start, "can only be used on functions"); + err_if_attr_found(ctxt, id, sym::rustc_main, "can only be used on functions"); } EntryPointType::MainNamed => (), EntryPointType::OtherMain => { + err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on root `fn main()`"); ctxt.non_main_fns.push(ctxt.tcx.def_span(id.def_id)); } EntryPointType::RustcMainAttr => { @@ -116,6 +116,7 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { } } EntryPointType::Start => { + err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on `fn main()`"); if ctxt.start_fn.is_none() { ctxt.start_fn = Some((id.def_id, ctxt.tcx.def_span(id.def_id.to_def_id()))); } else { @@ -136,8 +137,9 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, EntryFnType)> { if let Some((def_id, _)) = visitor.start_fn { Some((def_id.to_def_id(), EntryFnType::Start)) - } else if let Some((def_id, _)) = visitor.attr_main_fn { - Some((def_id.to_def_id(), EntryFnType::Main)) + } else if let Some((local_def_id, _)) = visitor.attr_main_fn { + let def_id = local_def_id.to_def_id(); + Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) })) } else { if let Some(main_def) = tcx.resolutions(()).main_def && let Some(def_id) = main_def.opt_fn_def_id() { // non-local main imports are handled below @@ -161,13 +163,39 @@ fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, ) .emit(); } - return Some((def_id, EntryFnType::Main)); + return Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) })); } no_main_err(tcx, visitor); None } } +fn sigpipe(tcx: TyCtxt<'_>, def_id: DefId) -> u8 { + if let Some(attr) = tcx.get_attr(def_id, sym::unix_sigpipe) { + match (attr.value_str(), attr.meta_item_list()) { + (Some(sym::inherit), None) => sigpipe::INHERIT, + (Some(sym::sig_ign), None) => sigpipe::SIG_IGN, + (Some(sym::sig_dfl), None) => sigpipe::SIG_DFL, + (_, Some(_)) => { + // Keep going so that `fn emit_malformed_attribute()` can print + // an excellent error message + sigpipe::DEFAULT + } + _ => { + tcx.sess + .struct_span_err( + attr.span, + "valid values for `#[unix_sigpipe = \"...\"]` are `inherit`, `sig_ign`, or `sig_dfl`", + ) + .emit(); + sigpipe::DEFAULT + } + } + } else { + sigpipe::DEFAULT + } +} + fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) { let sp = tcx.def_span(CRATE_DEF_ID); if *tcx.sess.parse_sess.reached_eof.borrow() { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 162fc9aa0a6..e7c8538300e 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -36,6 +36,8 @@ use std::iter::{self, FromIterator}; use std::path::{Path, PathBuf}; use std::str::{self, FromStr}; +pub mod sigpipe; + /// The different settings that the `-C strip` flag can have. #[derive(Clone, Copy, PartialEq, Hash, Debug)] pub enum Strip { @@ -798,7 +800,15 @@ impl UnstableOptions { // The type of entry function, so users can have their own entry functions #[derive(Copy, Clone, PartialEq, Hash, Debug, HashStable_Generic)] pub enum EntryFnType { - Main, + Main { + /// Specifies what to do with `SIGPIPE` before calling `fn main()`. + /// + /// What values that are valid and what they mean must be in sync + /// across rustc and libstd, but we don't want it public in libstd, + /// so we take a bit of an unusual approach with simple constants + /// and an `include!()`. + sigpipe: u8, + }, Start, } diff --git a/compiler/rustc_session/src/config/sigpipe.rs b/compiler/rustc_session/src/config/sigpipe.rs new file mode 100644 index 00000000000..31a3d9de05b --- /dev/null +++ b/compiler/rustc_session/src/config/sigpipe.rs @@ -0,0 +1,20 @@ +/// Do not touch `SIGPIPE`. Use whatever the parent process uses. +#[allow(dead_code)] +pub const INHERIT: u8 = 1; + +/// Change `SIGPIPE` to `SIG_IGN` so that failed writes results in `EPIPE` +/// that are eventually converted to `ErrorKind::BrokenPipe`. +#[allow(dead_code)] +pub const SIG_IGN: u8 = 2; + +/// Change `SIGPIPE` to `SIG_DFL` so that the process is killed when trying +/// to write to a closed pipe. This is usually the desired behavior for CLI +/// apps that produce textual output that you want to pipe to other programs +/// such as `head -n 1`. +#[allow(dead_code)] +pub const SIG_DFL: u8 = 3; + +/// `SIG_IGN` has been the Rust default since 2014. See +/// . +#[allow(dead_code)] +pub const DEFAULT: u8 = SIG_IGN; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1b47d11f6f8..92f1b2d4ea6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -819,6 +819,7 @@ symbols! { infer_outlives_requirements, infer_static_outlives_requirements, inherent_associated_types, + inherit, inlateout, inline, inline_const, @@ -1301,6 +1302,8 @@ symbols! { should_panic, shr, shr_assign, + sig_dfl, + sig_ign, simd, simd_add, simd_and, @@ -1519,6 +1522,7 @@ symbols! { unit, universal_impl_trait, unix, + unix_sigpipe, unlikely, unmarked_api, unpin, diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 5699f642baf..82b86d97d95 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -443,7 +443,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) { fn check_for_entry_fn(tcx: TyCtxt<'_>) { match tcx.entry_fn(()) { - Some((def_id, EntryFnType::Main)) => check_main_fn_ty(tcx, def_id), + Some((def_id, EntryFnType::Main { .. })) => check_main_fn_ty(tcx, def_id), Some((def_id, EntryFnType::Start)) => check_start_fn_ty(tcx, def_id), _ => {} } diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 663537a05fa..7cf3d7d4163 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -73,9 +73,9 @@ macro_rules! rtunwrap { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. #[cfg_attr(test, allow(dead_code))] -unsafe fn init(argc: isize, argv: *const *const u8) { +unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { unsafe { - sys::init(argc, argv); + sys::init(argc, argv, sigpipe); let main_guard = sys::thread::guard::init(); // Next, set up the current Thread with the guard information we just @@ -107,6 +107,7 @@ fn lang_start_internal( main: &(dyn Fn() -> i32 + Sync + crate::panic::RefUnwindSafe), argc: isize, argv: *const *const u8, + sigpipe: u8, ) -> Result { use crate::{mem, panic}; let rt_abort = move |e| { @@ -124,7 +125,7 @@ fn lang_start_internal( // prevent libstd from accidentally introducing a panic to these functions. Another is from // user code from `main` or, more nefariously, as described in e.g. issue #86030. // SAFETY: Only called once during runtime initialization. - panic::catch_unwind(move || unsafe { init(argc, argv) }).map_err(rt_abort)?; + panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }).map_err(rt_abort)?; let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize) .map_err(move |e| { mem::forget(e); @@ -140,11 +141,16 @@ fn lang_start( main: fn() -> T, argc: isize, argv: *const *const u8, + #[cfg(not(bootstrap))] sigpipe: u8, ) -> isize { let Ok(v) = lang_start_internal( &move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report().to_i32(), argc, argv, + #[cfg(bootstrap)] + 2, // Temporary inlining of sigpipe::DEFAULT until bootstrap stops being special + #[cfg(not(bootstrap))] + sigpipe, ); v } diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index 60b7a973cc2..61da096ae16 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -98,7 +98,7 @@ pub extern "C" fn __rust_abort() { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. -pub unsafe fn init(argc: isize, argv: *const *const u8) { +pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { let _ = net::init(); args::init(argc, argv); } diff --git a/library/std/src/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs index 696400670e0..b1d32929ecf 100644 --- a/library/std/src/sys/sgx/mod.rs +++ b/library/std/src/sys/sgx/mod.rs @@ -47,7 +47,7 @@ pub mod locks { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. -pub unsafe fn init(argc: isize, argv: *const *const u8) { +pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { unsafe { args::init(argc, argv); } diff --git a/library/std/src/sys/solid/mod.rs b/library/std/src/sys/solid/mod.rs index 778a589d1b7..5867979a2a7 100644 --- a/library/std/src/sys/solid/mod.rs +++ b/library/std/src/sys/solid/mod.rs @@ -56,7 +56,7 @@ pub mod locks { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. -pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} +pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} // SAFETY: must be called only once during runtime cleanup. pub unsafe fn cleanup() {} diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 3a375093099..e11201f60ff 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -44,12 +44,12 @@ pub mod thread_parker; pub mod time; #[cfg(target_os = "espidf")] -pub fn init(argc: isize, argv: *const *const u8) {} +pub fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {} #[cfg(not(target_os = "espidf"))] // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. -pub unsafe fn init(argc: isize, argv: *const *const u8) { +pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { // The standard streams might be closed on application startup. To prevent // std::io::{stdin, stdout,stderr} objects from using other unrelated file // resources opened later, we reopen standards streams when they are closed. @@ -61,8 +61,9 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) { // want! // // Hence, we set SIGPIPE to ignore when the program starts up in order - // to prevent this problem. - reset_sigpipe(); + // to prevent this problem. Add `#[unix_sigpipe = "..."]` above `fn main()` to + // alter this behavior. + reset_sigpipe(sigpipe); stack_overflow::init(); args::init(argc, argv); @@ -151,9 +152,25 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) { } } - unsafe fn reset_sigpipe() { + unsafe fn reset_sigpipe(#[allow(unused_variables)] sigpipe: u8) { #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))] - rtassert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR); + { + // We don't want to add this as a public type to libstd, nor do we want to + // duplicate the code, so we choose to include this compiler file like this. + mod sigpipe { + include!("../../../../../compiler/rustc_session/src/config/sigpipe.rs"); + } + + let handler = match sigpipe { + sigpipe::INHERIT => None, + sigpipe::SIG_IGN => Some(libc::SIG_IGN), + sigpipe::SIG_DFL => Some(libc::SIG_DFL), + _ => unreachable!(), + }; + if let Some(handler) = handler { + rtassert!(signal(libc::SIGPIPE, handler) != libc::SIG_ERR); + } + } } } diff --git a/library/std/src/sys/unsupported/common.rs b/library/std/src/sys/unsupported/common.rs index 4c9ade4a8c7..5cd9e57de19 100644 --- a/library/std/src/sys/unsupported/common.rs +++ b/library/std/src/sys/unsupported/common.rs @@ -6,7 +6,7 @@ pub mod memchr { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. -pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} +pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index a9846a48488..510f470bfe1 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -48,7 +48,7 @@ cfg_if::cfg_if! { // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. -pub unsafe fn init(_argc: isize, _argv: *const *const u8) { +pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { stack_overflow::init(); // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already diff --git a/src/doc/unstable-book/src/language-features/unix-sigpipe.md b/src/doc/unstable-book/src/language-features/unix-sigpipe.md new file mode 100644 index 00000000000..aa39b6eb288 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/unix-sigpipe.md @@ -0,0 +1,54 @@ +# `unix_sigpipe` + +The tracking issue for this feature is: [#97889] + +[#97889]: https://github.com/rust-lang/rust/issues/97889 + +--- + +The `#[unix_sigpipe = "..."]` attribute on `fn main()` can be used to specify how libstd shall setup `SIGPIPE` on Unix platforms before invoking `fn main()`. This attribute is ignored on non-Unix targets. There are three variants: +* `#[unix_sigpipe = "inherit"]` +* `#[unix_sigpipe = "sig_dfl"]` +* `#[unix_sigpipe = "sig_ign"]` + +## `#[unix_sigpipe = "inherit"]` + +Leave `SIGPIPE` untouched before entering `fn main()`. Unless the parent process has changed the default `SIGPIPE` handler from `SIG_DFL` to something else, this will behave the same as `#[unix_sigpipe = "sig_dfl"]`. + +## `#[unix_sigpipe = "sig_dfl"]` + +Set the `SIGPIPE` handler to `SIG_DFL`. This will result in your program getting killed if it tries to write to a closed pipe. This is normally what you want if your program produces textual output. + +### Example + +```rust,no_run +#![feature(unix_sigpipe)] +#[unix_sigpipe = "sig_dfl"] +fn main() { loop { println!("hello world"); } } +``` + +```bash +% ./main | head -n 1 +hello world +``` + +## `#[unix_sigpipe = "sig_ign"]` + +Set the `SIGPIPE` handler to `SIG_IGN` before invoking `fn main()`. This will result in `ErrorKind::BrokenPipe` errors if you program tries to write to a closed pipe. This is normally what you want if you for example write socket servers, socket clients, or pipe peers. + +This is what libstd has done by default since 2014. Omitting `#[unix_sigpipe = "..."]` is the same as having `#[unix_sigpipe = "sig_ign"]`. + +### Example + +```rust,no_run +#![feature(unix_sigpipe)] +#[unix_sigpipe = "sig_ign"] +fn main() { loop { println!("hello world"); } } +``` + +```bash +% ./main | head -n 1 +hello world +thread 'main' panicked at 'failed printing to stdout: Broken pipe (os error 32)', library/std/src/io/stdio.rs:1016:9 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +``` diff --git a/src/test/ui/attributes/unix_sigpipe/auxiliary/sigpipe-utils.rs b/src/test/ui/attributes/unix_sigpipe/auxiliary/sigpipe-utils.rs new file mode 100644 index 00000000000..07ff051f789 --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/auxiliary/sigpipe-utils.rs @@ -0,0 +1,26 @@ +#![feature(rustc_private)] +extern crate libc; + +/// So tests don't have to bring libc in scope themselves +pub enum SignalHandler { + Ignore, + Default, +} + +/// Helper to assert that [`libc::SIGPIPE`] has the expected signal handler. +pub fn assert_sigpipe_handler(expected_handler: SignalHandler) { + #[cfg(unix)] + #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))] + { + let prev = unsafe { libc::signal(libc::SIGPIPE, libc::SIG_IGN) }; + + let expected = match expected_handler { + SignalHandler::Ignore => libc::SIG_IGN, + SignalHandler::Default => libc::SIG_DFL, + }; + assert_eq!(prev, expected); + + // Unlikely to matter, but restore the old value anyway + unsafe { libc::signal(libc::SIGPIPE, prev); }; + } +} diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-crate.rs b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-crate.rs new file mode 100644 index 00000000000..d6d020c52b2 --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-crate.rs @@ -0,0 +1,4 @@ +#![feature(unix_sigpipe)] +#![unix_sigpipe = "inherit"] //~ error: `unix_sigpipe` attribute cannot be used at crate level + +fn main() {} diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-crate.stderr b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-crate.stderr new file mode 100644 index 00000000000..a1fb4d6787c --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-crate.stderr @@ -0,0 +1,13 @@ +error: `unix_sigpipe` attribute cannot be used at crate level + --> $DIR/unix_sigpipe-crate.rs:2:1 + | +LL | #![unix_sigpipe = "inherit"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: perhaps you meant to use an outer attribute + | +LL | #[unix_sigpipe = "inherit"] + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to previous error + diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-duplicates.rs b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-duplicates.rs new file mode 100644 index 00000000000..294cb38526b --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-duplicates.rs @@ -0,0 +1,5 @@ +#![feature(unix_sigpipe)] + +#[unix_sigpipe = "sig_ign"] +#[unix_sigpipe = "inherit"] //~ error: multiple `unix_sigpipe` attributes +fn main() {} diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-duplicates.stderr b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-duplicates.stderr new file mode 100644 index 00000000000..2362c17a090 --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-duplicates.stderr @@ -0,0 +1,14 @@ +error: multiple `unix_sigpipe` attributes + --> $DIR/unix_sigpipe-duplicates.rs:4:1 + | +LL | #[unix_sigpipe = "inherit"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unix_sigpipe-duplicates.rs:3:1 + | +LL | #[unix_sigpipe = "sig_ign"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-error.rs b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-error.rs new file mode 100644 index 00000000000..0a42a5b5ef1 --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-error.rs @@ -0,0 +1,13 @@ +// run-pass +// aux-build:sigpipe-utils.rs + +#![feature(unix_sigpipe)] + +#[unix_sigpipe = "sig_ign"] +fn main() { + extern crate sigpipe_utils; + + // #[unix_sigpipe = "sig_ign"] is active, so the legacy behavior of ignoring + // SIGPIPE shall be in effect + sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore); +} diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-inherit.rs b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-inherit.rs new file mode 100644 index 00000000000..4f864807752 --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-inherit.rs @@ -0,0 +1,14 @@ +// run-pass +// aux-build:sigpipe-utils.rs + +#![feature(unix_sigpipe)] + +#[unix_sigpipe = "inherit"] +fn main() { + extern crate sigpipe_utils; + + // #[unix_sigpipe = "inherit"] is active, so SIGPIPE shall NOT be ignored, + // instead the default handler shall be installed. (We assume that the + // process that runs these tests have the default handler.) + sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default); +} diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-list.rs b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-list.rs new file mode 100644 index 00000000000..b5ebc07a043 --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-list.rs @@ -0,0 +1,4 @@ +#![feature(unix_sigpipe)] + +#[unix_sigpipe(inherit)] //~ error: malformed `unix_sigpipe` attribute input +fn main() {} diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-list.stderr b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-list.stderr new file mode 100644 index 00000000000..59a87e13918 --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-list.stderr @@ -0,0 +1,15 @@ +error: malformed `unix_sigpipe` attribute input + --> $DIR/unix_sigpipe-list.rs:3:1 + | +LL | #[unix_sigpipe(inherit)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the following are the possible correct uses + | +LL | #[unix_sigpipe = "inherit|sig_ign|sig_dfl"] + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | #[unix_sigpipe] + | ~~~~~~~~~~~~~~~ + +error: aborting due to previous error + diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-main-fn.rs b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-main-fn.rs new file mode 100644 index 00000000000..cde6719fc9c --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-main-fn.rs @@ -0,0 +1,6 @@ +#![feature(unix_sigpipe)] + +#[unix_sigpipe = "inherit"] //~ error: `unix_sigpipe` attribute can only be used on `fn main()` +fn f() {} + +fn main() {} diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-main-fn.stderr b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-main-fn.stderr new file mode 100644 index 00000000000..c4b81118c9f --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-main-fn.stderr @@ -0,0 +1,8 @@ +error: `unix_sigpipe` attribute can only be used on `fn main()` + --> $DIR/unix_sigpipe-non-main-fn.rs:3:1 + | +LL | #[unix_sigpipe = "inherit"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-root-main.rs b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-root-main.rs new file mode 100644 index 00000000000..16f7276398e --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-root-main.rs @@ -0,0 +1,8 @@ +#![feature(unix_sigpipe)] + +mod m { + #[unix_sigpipe = "inherit"] //~ error: `unix_sigpipe` attribute can only be used on root `fn main()` + fn main() {} +} + +fn main() {} diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-root-main.stderr b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-root-main.stderr new file mode 100644 index 00000000000..a04f605edc2 --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-non-root-main.stderr @@ -0,0 +1,8 @@ +error: `unix_sigpipe` attribute can only be used on root `fn main()` + --> $DIR/unix_sigpipe-non-root-main.rs:4:5 + | +LL | #[unix_sigpipe = "inherit"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-not-used.rs b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-not-used.rs new file mode 100644 index 00000000000..100b4ce9f74 --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-not-used.rs @@ -0,0 +1,9 @@ +// run-pass +// aux-build:sigpipe-utils.rs + +fn main() { + extern crate sigpipe_utils; + + // SIGPIPE shall be ignored since #[unix_sigpipe = "..."] is not used + sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore); +} diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-only-feature.rs b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-only-feature.rs new file mode 100644 index 00000000000..b5adc2e5572 --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-only-feature.rs @@ -0,0 +1,13 @@ +// run-pass +// aux-build:sigpipe-utils.rs + +#![feature(unix_sigpipe)] + +fn main() { + extern crate sigpipe_utils; + + // Only #![feature(unix_sigpipe)] is enabled, not #[unix_sigpipe = "..."]. + // This shall not change any behavior, so we still expect SIGPIPE to be + // ignored + sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore); +} diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-rustc_main.rs b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-rustc_main.rs new file mode 100644 index 00000000000..6befb9e9565 --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-rustc_main.rs @@ -0,0 +1,15 @@ +// run-pass +// aux-build:sigpipe-utils.rs + +#![feature(unix_sigpipe)] +#![feature(rustc_attrs)] + +#[unix_sigpipe = "sig_dfl"] +#[rustc_main] +fn rustc_main() { + extern crate sigpipe_utils; + + // #[unix_sigpipe = "sig_dfl"] is active, so SIGPIPE handler shall be + // SIG_DFL. Note that we have a #[rustc_main], but it should still work. + sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default); +} diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-sig_dfl.rs b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-sig_dfl.rs new file mode 100644 index 00000000000..238c0d57a68 --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-sig_dfl.rs @@ -0,0 +1,13 @@ +// run-pass +// aux-build:sigpipe-utils.rs + +#![feature(unix_sigpipe)] + +#[unix_sigpipe = "sig_dfl"] +fn main() { + extern crate sigpipe_utils; + + // #[unix_sigpipe = "sig_dfl"] is active, so SIGPIPE shall NOT be ignored, instead + // the default handler shall be installed + sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default); +} diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-start.rs b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-start.rs new file mode 100644 index 00000000000..64fd5ec4f0e --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-start.rs @@ -0,0 +1,6 @@ +#![feature(start)] +#![feature(unix_sigpipe)] + +#[start] +#[unix_sigpipe = "inherit"] //~ error: `unix_sigpipe` attribute can only be used on `fn main()` +fn custom_start(argc: isize, argv: *const *const u8) -> isize { 0 } diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-start.stderr b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-start.stderr new file mode 100644 index 00000000000..2c9ce479b6c --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-start.stderr @@ -0,0 +1,8 @@ +error: `unix_sigpipe` attribute can only be used on `fn main()` + --> $DIR/unix_sigpipe-start.rs:5:1 + | +LL | #[unix_sigpipe = "inherit"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-struct.rs b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-struct.rs new file mode 100644 index 00000000000..a5e47cfebc8 --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-struct.rs @@ -0,0 +1,6 @@ +#![feature(unix_sigpipe)] + +#[unix_sigpipe = "inherit"] //~ error: `unix_sigpipe` attribute can only be used on `fn main()` +struct S; + +fn main() {} diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-struct.stderr b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-struct.stderr new file mode 100644 index 00000000000..c56ee60bb2e --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-struct.stderr @@ -0,0 +1,8 @@ +error: `unix_sigpipe` attribute can only be used on `fn main()` + --> $DIR/unix_sigpipe-struct.rs:3:1 + | +LL | #[unix_sigpipe = "inherit"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-wrong.rs b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-wrong.rs new file mode 100644 index 00000000000..4ec25de00ec --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-wrong.rs @@ -0,0 +1,4 @@ +#![feature(unix_sigpipe)] + +#[unix_sigpipe = "wrong"] //~ error: valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl` +fn main() {} diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-wrong.stderr b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-wrong.stderr new file mode 100644 index 00000000000..a66e45aa210 --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe-wrong.stderr @@ -0,0 +1,8 @@ +error: valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl` + --> $DIR/unix_sigpipe-wrong.rs:3:1 + | +LL | #[unix_sigpipe = "wrong"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe.rs b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe.rs new file mode 100644 index 00000000000..7bf1c7350c3 --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe.rs @@ -0,0 +1,4 @@ +#![feature(unix_sigpipe)] + +#[unix_sigpipe] //~ error: valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl` +fn main() {} diff --git a/src/test/ui/attributes/unix_sigpipe/unix_sigpipe.stderr b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe.stderr new file mode 100644 index 00000000000..1b1eda825aa --- /dev/null +++ b/src/test/ui/attributes/unix_sigpipe/unix_sigpipe.stderr @@ -0,0 +1,8 @@ +error: valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl` + --> $DIR/unix_sigpipe.rs:3:1 + | +LL | #[unix_sigpipe] + | ^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/feature-gates/feature-gate-unix_sigpipe.rs b/src/test/ui/feature-gates/feature-gate-unix_sigpipe.rs new file mode 100644 index 00000000000..46dc3f6cc17 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-unix_sigpipe.rs @@ -0,0 +1,4 @@ +#![crate_type = "bin"] + +#[unix_sigpipe = "inherit"] //~ the `#[unix_sigpipe]` attribute is an experimental feature +fn main () {} diff --git a/src/test/ui/feature-gates/feature-gate-unix_sigpipe.stderr b/src/test/ui/feature-gates/feature-gate-unix_sigpipe.stderr new file mode 100644 index 00000000000..cf3284467f7 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-unix_sigpipe.stderr @@ -0,0 +1,12 @@ +error[E0658]: the `#[unix_sigpipe]` attribute is an experimental feature + --> $DIR/feature-gate-unix_sigpipe.rs:3:1 + | +LL | #[unix_sigpipe = "inherit"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #97889 for more information + = help: add `#![feature(unix_sigpipe)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. From 6eae169ca2206671779b5b405052417a182a47e9 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 29 Aug 2022 20:39:23 +0200 Subject: [PATCH 2/5] unix_sigpipe: Skip some tests on android; libc has no `signal` function If we don't skip these tests, they will fail in CI when `python3 ../x.py --stage 2 test --host= --target arm-linux-androideabi` runs. The failure is: auxiliary/libsigpipe_utils.so: error: undefined reference to 'signal' --- .../ui/attributes/unix_sigpipe/auxiliary/sigpipe-utils.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/test/ui/attributes/unix_sigpipe/auxiliary/sigpipe-utils.rs b/src/test/ui/attributes/unix_sigpipe/auxiliary/sigpipe-utils.rs index 07ff051f789..e8b4fe7ae52 100644 --- a/src/test/ui/attributes/unix_sigpipe/auxiliary/sigpipe-utils.rs +++ b/src/test/ui/attributes/unix_sigpipe/auxiliary/sigpipe-utils.rs @@ -10,7 +10,12 @@ pub enum SignalHandler { /// Helper to assert that [`libc::SIGPIPE`] has the expected signal handler. pub fn assert_sigpipe_handler(expected_handler: SignalHandler) { #[cfg(unix)] - #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))] + #[cfg(not(any( + target_os = "emscripten", + target_os = "fuchsia", + target_os = "horizon", + target_os = "android", + )))] { let prev = unsafe { libc::signal(libc::SIGPIPE, libc::SIG_IGN) }; From 236903f7e9045717d7795ab12bdf349ccf276c3b Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Wed, 31 Aug 2022 18:04:15 +0200 Subject: [PATCH 3/5] unix_sigpipe: Inline compiler sigpipe constants in std --- compiler/rustc_session/src/config/sigpipe.rs | 2 ++ library/std/src/sys/unix/mod.rs | 12 +++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_session/src/config/sigpipe.rs b/compiler/rustc_session/src/config/sigpipe.rs index 31a3d9de05b..a5c94118a47 100644 --- a/compiler/rustc_session/src/config/sigpipe.rs +++ b/compiler/rustc_session/src/config/sigpipe.rs @@ -1,3 +1,5 @@ +//! NOTE: Keep these constants in sync with `library/std/src/sys/unix/mod.rs`! + /// Do not touch `SIGPIPE`. Use whatever the parent process uses. #[allow(dead_code)] pub const INHERIT: u8 = 1; diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index e11201f60ff..d701f950e86 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -155,10 +155,16 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { unsafe fn reset_sigpipe(#[allow(unused_variables)] sigpipe: u8) { #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))] { - // We don't want to add this as a public type to libstd, nor do we want to - // duplicate the code, so we choose to include this compiler file like this. + // We don't want to add this as a public type to libstd, nor do we + // want to `include!` a file from the compiler (which would break + // Miri and xargo for example), so we choose to duplicate these + // constants from `compiler/rustc_session/src/config/sigpipe.rs`. + // See the other file for docs. NOTE: Make sure to keep them in + // sync! mod sigpipe { - include!("../../../../../compiler/rustc_session/src/config/sigpipe.rs"); + pub const INHERIT: u8 = 1; + pub const SIG_IGN: u8 = 2; + pub const SIG_DFL: u8 = 3; } let handler = match sigpipe { From 3d1a4e4f2792948d78b3fed030e93c9c156fe35a Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Wed, 31 Aug 2022 18:11:48 +0200 Subject: [PATCH 4/5] unix_sigpipe: Add docs for `init()` `sigpipe` param --- library/std/src/rt.rs | 2 ++ library/std/src/sys/unix/mod.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 7cf3d7d4163..b3f6f82952b 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -72,6 +72,8 @@ macro_rules! rtunwrap { // Runs before `main`. // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. +// The extra parameter `sigpipe` allows rustc to generate code that instructs std whether +// or not to ignore `SIGPIPE`. #[cfg_attr(test, allow(dead_code))] unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { unsafe { diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index d701f950e86..2856dfcb370 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -49,6 +49,8 @@ pub fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {} #[cfg(not(target_os = "espidf"))] // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. +// The extra parameter `sigpipe` allows rustc to generate code that instructs std whether +// or not to ignore `SIGPIPE`. pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { // The standard streams might be closed on application startup. To prevent // std::io::{stdin, stdout,stderr} objects from using other unrelated file From 3810d4a3680d825448f5034262cc6ad63586b02e Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Thu, 1 Sep 2022 06:43:03 +0200 Subject: [PATCH 5/5] unix_sigpipe: Make `sigpipe` param docs long-form --- library/std/src/rt.rs | 21 +++++++++++++++++++-- library/std/src/sys/unix/mod.rs | 3 +-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index b3f6f82952b..98f6cc7aa3e 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -72,8 +72,25 @@ macro_rules! rtunwrap { // Runs before `main`. // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. -// The extra parameter `sigpipe` allows rustc to generate code that instructs std whether -// or not to ignore `SIGPIPE`. +// +// # The `sigpipe` parameter +// +// Since 2014, the Rust runtime on Unix has set the `SIGPIPE` handler to +// `SIG_IGN`. Applications have good reasons to want a different behavior +// though, so there is a `#[unix_sigpipe = "..."]` attribute on `fn main()` that +// can be used to select how `SIGPIPE` shall be setup (if changed at all) before +// `fn main()` is called. See +// for more info. +// +// The `sigpipe` parameter to this function gets its value via the code that +// rustc generates to invoke `fn lang_start()`. The reason we have `sigpipe` for +// all platforms and not only Unix, is because std is not allowed to have `cfg` +// directives as this high level. See the module docs in +// `src/tools/tidy/src/pal.rs` for more info. On all other platforms, `sigpipe` +// has a value, but its value is ignored. +// +// Even though it is an `u8`, it only ever has 3 values. These are documented in +// `compiler/rustc_session/src/config/sigpipe.rs`. #[cfg_attr(test, allow(dead_code))] unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { unsafe { diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 2856dfcb370..c84e292eac1 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -49,8 +49,7 @@ pub fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {} #[cfg(not(target_os = "espidf"))] // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. -// The extra parameter `sigpipe` allows rustc to generate code that instructs std whether -// or not to ignore `SIGPIPE`. +// See `fn init()` in `library/std/src/rt.rs` for docs on `sigpipe`. pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { // The standard streams might be closed on application startup. To prevent // std::io::{stdin, stdout,stderr} objects from using other unrelated file