@@ -10,6 +10,8 @@ use std::path::Path;
10
10
use std:: sync:: atomic:: { AtomicPtr , Ordering } ;
11
11
12
12
use crate :: modes:: unix:: * ;
13
+ use nix:: sys:: socket;
14
+ use std:: os:: unix:: io:: AsRawFd ;
13
15
14
16
/// Defines the additional behavior for a given crashtracking test
15
17
pub trait Behavior {
@@ -89,6 +91,32 @@ pub fn remove_permissive(filepath: &Path) {
89
91
let _ = std:: fs:: remove_file ( filepath) ;
90
92
}
91
93
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
+
92
120
pub fn get_behavior ( mode_str : & str ) -> Box < dyn Behavior > {
93
121
match mode_str {
94
122
"donothing" => Box :: new ( test_000_donothing:: Test ) ,
@@ -104,3 +132,77 @@ pub fn get_behavior(mode_str: &str) -> Box<dyn Behavior> {
104
132
_ => panic ! ( "Unknown mode: {mode_str}" ) ,
105
133
}
106
134
}
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