@@ -10,6 +10,8 @@ use std::path::Path;
1010use std:: sync:: atomic:: { AtomicPtr , Ordering } ;
1111
1212use crate :: modes:: unix:: * ;
13+ use nix:: sys:: socket;
14+ use std:: os:: unix:: io:: AsRawFd ;
1315
1416/// Defines the additional behavior for a given crashtracking test
1517pub trait Behavior {
@@ -89,6 +91,32 @@ pub fn remove_permissive(filepath: &Path) {
8991 let _ = std:: fs:: remove_file ( filepath) ;
9092}
9193
94+ // This helper function is used to trigger a SIGPIPE signal. This is useful to
95+ // verify that the crashtracker correctly suppresses the SIGPIPE signal while it
96+ // emitts information to the collector, and that the SIGPIPE signal can be emitted
97+ // and used normally afterwards, as tested in the following tests:
98+ // - test_001_sigpipe
99+ // - test_005_sigpipe_sigstack
100+ pub fn trigger_sigpipe ( ) -> Result < ( ) > {
101+ let ( reader_fd, writer_fd) = socket:: socketpair (
102+ socket:: AddressFamily :: Unix ,
103+ socket:: SockType :: Stream ,
104+ None ,
105+ socket:: SockFlag :: empty ( ) ,
106+ ) ?;
107+ drop ( reader_fd) ;
108+
109+ let writer_raw_fd = writer_fd. as_raw_fd ( ) ;
110+ let write_result =
111+ unsafe { libc:: write ( writer_raw_fd, b"Hello" . as_ptr ( ) as * const libc:: c_void , 5 ) } ;
112+
113+ if write_result != -1 {
114+ anyhow:: bail!( "Expected write to fail with SIGPIPE, but it succeeded" ) ;
115+ }
116+
117+ Ok ( ( ) )
118+ }
119+
92120pub fn get_behavior ( mode_str : & str ) -> Box < dyn Behavior > {
93121 match mode_str {
94122 "donothing" => Box :: new ( test_000_donothing:: Test ) ,
@@ -104,3 +132,77 @@ pub fn get_behavior(mode_str: &str) -> Box<dyn Behavior> {
104132 _ => panic ! ( "Unknown mode: {mode_str}" ) ,
105133 }
106134}
135+
136+ #[ cfg( test) ]
137+ mod tests {
138+ use super :: * ;
139+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
140+
141+ static SIGPIPE_CAUGHT : AtomicBool = AtomicBool :: new ( false ) ;
142+
143+ extern "C" fn sigpipe_handler ( _: libc:: c_int ) {
144+ SIGPIPE_CAUGHT . store ( true , Ordering :: SeqCst ) ;
145+ }
146+
147+ #[ test]
148+ #[ cfg_attr( miri, ignore) ]
149+ fn test_trigger_sigpipe ( ) {
150+ use std:: mem;
151+ use std:: thread;
152+
153+ let result = thread:: spawn ( || {
154+ SIGPIPE_CAUGHT . store ( false , Ordering :: SeqCst ) ;
155+
156+ let mut sigset: libc:: sigset_t = unsafe { mem:: zeroed ( ) } ;
157+ unsafe {
158+ libc:: sigemptyset ( & mut sigset) ;
159+ }
160+
161+ let sigpipe_action = libc:: sigaction {
162+ sa_sigaction : sigpipe_handler as usize ,
163+ sa_mask : sigset,
164+ sa_flags : libc:: SA_RESTART | libc:: SA_SIGINFO ,
165+ #[ cfg( target_os = "linux" ) ]
166+ sa_restorer : None ,
167+ } ;
168+
169+ let mut old_action: libc:: sigaction = unsafe { mem:: zeroed ( ) } ;
170+ let install_result =
171+ unsafe { libc:: sigaction ( libc:: SIGPIPE , & sigpipe_action, & mut old_action) } ;
172+
173+ if install_result != 0 {
174+ return Err ( "Failed to set up SIGPIPE handler" . to_string ( ) ) ;
175+ }
176+
177+ let trigger_result = trigger_sigpipe ( ) ;
178+
179+ thread:: sleep ( std:: time:: Duration :: from_millis ( 10 ) ) ;
180+
181+ let handler_called = SIGPIPE_CAUGHT . load ( Ordering :: SeqCst ) ;
182+
183+ unsafe {
184+ libc:: sigaction ( libc:: SIGPIPE , & old_action, std:: ptr:: null_mut ( ) ) ;
185+ }
186+
187+ if trigger_result. is_err ( ) {
188+ return Err ( format ! (
189+ "trigger_sigpipe should succeed: {:?}" ,
190+ trigger_result
191+ ) ) ;
192+ }
193+
194+ if !handler_called {
195+ return Err ( "SIGPIPE handler should have been called" . to_string ( ) ) ;
196+ }
197+
198+ Ok ( ( ) )
199+ } )
200+ . join ( ) ;
201+
202+ match result {
203+ Ok ( Ok ( ( ) ) ) => { } // Test passed
204+ Ok ( Err ( e) ) => panic ! ( "{}" , e) ,
205+ Err ( _) => panic ! ( "Thread panicked during SIGPIPE test" ) ,
206+ }
207+ }
208+ }
0 commit comments