1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 #![cfg(feature = "linux")]
15
16 use std::mem::MaybeUninit;
17 use std::os::raw::c_int;
18 use std::ptr::null_mut;
19 use std::sync::atomic::{AtomicUsize, Ordering};
20 use std::sync::{Arc, Once};
21 use std::{io, mem, thread};
22
23 /// SDV cases for signal register
24 ///
25 /// # Brief
26 /// 1. Registers two different signals with actions that increment two different
27 /// atomic usize.
28 /// 2. Manually raises the two signals, checks if the registered action behave
29 /// correctly.
30 /// 3. Deregisters the action of the two signals
31 /// 4. Manually raises the two signals, the actions should not be executed, and
32 /// the program should not be terminated
33 /// 5. Registers the same action for one of the signals again
34 /// 6. Manually raises the signal, checks if the registered action behave
35 /// correctly
36 /// 7. Deregisters both signal's handler hook, checks if the return is ok.
37 #[test]
sdv_signal_register_succeed()38 fn sdv_signal_register_succeed() {
39 let value = Arc::new(AtomicUsize::new(0));
40 let value_cpy = value.clone();
41
42 let value2 = Arc::new(AtomicUsize::new(10));
43 let value2_cpy = value2.clone();
44 let value2_cpy2 = value2.clone();
45
46 let res = unsafe {
47 ylong_signal::register_signal_action(libc::SIGINT, move || {
48 value_cpy.fetch_add(1, Ordering::Relaxed);
49 })
50 };
51 assert!(res.is_ok());
52
53 let res2 = unsafe {
54 ylong_signal::register_signal_action(libc::SIGTERM, move || {
55 value2_cpy.fetch_add(10, Ordering::Relaxed);
56 })
57 };
58 assert!(res2.is_ok());
59 assert_eq!(value.load(Ordering::Relaxed), 0);
60
61 unsafe { libc::raise(libc::SIGINT) };
62 assert_eq!(value.load(Ordering::Relaxed), 1);
63 assert_eq!(value2.load(Ordering::Relaxed), 10);
64
65 unsafe { libc::raise(libc::SIGTERM) };
66 assert_eq!(value.load(Ordering::Relaxed), 1);
67 assert_eq!(value2.load(Ordering::Relaxed), 20);
68
69 ylong_signal::deregister_signal_action(libc::SIGTERM);
70 unsafe { libc::raise(libc::SIGTERM) };
71 assert_eq!(value2.load(Ordering::Relaxed), 20);
72
73 ylong_signal::deregister_signal_action(libc::SIGINT);
74
75 let res3 = unsafe {
76 ylong_signal::register_signal_action(libc::SIGTERM, move || {
77 value2_cpy2.fetch_add(20, Ordering::Relaxed);
78 })
79 };
80 assert!(res3.is_ok());
81 unsafe { libc::raise(libc::SIGTERM) };
82 assert_eq!(value2.load(Ordering::Relaxed), 40);
83
84 let res4 = ylong_signal::deregister_signal_hook(libc::SIGTERM);
85 assert!(res4.is_ok());
86
87 let res5 = ylong_signal::deregister_signal_hook(libc::SIGINT);
88 assert!(res5.is_ok());
89 }
90
91 /// SDV cases for signal register error handling
92 ///
93 /// # Brief
94 /// 1. Registers an action for a forbidden signal
95 /// 2. Checks if the return value is InvalidInput error
96 /// 3. Registers an action for an allowed signal
97 /// 4. Checks if the return value is Ok
98 /// 5. Registers an action for the same signal again
99 /// 6. Checks if the return value is AlreadyExists error
100 /// 7. Deregisters the signal hook of the previous registered signal
101 /// 8. Checks if the return value is OK
102 /// 9. Deregisters the signal action of an unregistered signal
103 /// 10. Deregisters the signal handler of an unregistered signal
104 /// 11. Checks if the return value is Ok
105 #[test]
sdv_signal_register_failed()106 fn sdv_signal_register_failed() {
107 let res = unsafe { ylong_signal::register_signal_action(libc::SIGSEGV, move || {}) };
108 assert_eq!(res.unwrap_err().kind(), io::ErrorKind::InvalidInput);
109
110 let res = unsafe { ylong_signal::register_signal_action(libc::SIGQUIT, move || {}) };
111 assert!(res.is_ok());
112 let res = unsafe { ylong_signal::register_signal_action(libc::SIGQUIT, move || {}) };
113 assert_eq!(res.unwrap_err().kind(), io::ErrorKind::AlreadyExists);
114
115 let res = ylong_signal::deregister_signal_hook(libc::SIGQUIT);
116 assert!(res.is_ok());
117
118 ylong_signal::deregister_signal_action(libc::SIG_UNBLOCK);
119 let res = ylong_signal::deregister_signal_hook(libc::SIG_UNBLOCK);
120 assert!(res.is_ok());
121 }
122
123 /// SDV cases for signal register when there is already an existing handler
124 ///
125 /// # Brief
126 /// 1. Registers a signal handler using libc syscall
127 /// 2. Registers a signal handler using ylong_signal::register_signal_action
128 /// 3. Manually raises the signal
129 /// 4. Checks if the old handler and the new action both get executed correctly
130 /// 5. Deregisters the signal action
131 /// 6. Manually raises the signal
132 /// 7. Checks if the old handler gets executed correctly
133 /// 8. Deregister the hook.
134 #[test]
sdv_signal_register_with_old()135 fn sdv_signal_register_with_old() {
136 let mut new_act: libc::sigaction = unsafe { mem::zeroed() };
137 new_act.sa_sigaction = test_handler as usize;
138 unsafe {
139 libc::sigaction(libc::SIGCONT, &new_act, null_mut());
140 }
141
142 let res = unsafe {
143 ylong_signal::register_signal_action(libc::SIGCONT, move || {
144 let global = Global::get_instance();
145 assert_eq!(global.value.load(Ordering::Relaxed), 1);
146 global.value.fetch_add(2, Ordering::Relaxed);
147 })
148 };
149 assert!(res.is_ok());
150 unsafe {
151 libc::raise(libc::SIGCONT);
152 }
153 let global = Global::get_instance();
154 assert_eq!(global.value.load(Ordering::Relaxed), 3);
155
156 ylong_signal::deregister_signal_action(libc::SIGCONT);
157 unsafe {
158 libc::raise(libc::SIGCONT);
159 }
160 assert_eq!(global.value.load(Ordering::Relaxed), 4);
161 let res = ylong_signal::deregister_signal_hook(libc::SIGCONT);
162 assert!(res.is_ok());
163
164 unsafe {
165 libc::raise(libc::SIGCONT);
166 }
167 }
168
169 pub struct Global {
170 value: AtomicUsize,
171 }
172
173 impl Global {
get_instance() -> &'static Global174 fn get_instance() -> &'static Global {
175 static mut GLOBAL: MaybeUninit<Global> = MaybeUninit::uninit();
176 static ONCE: Once = Once::new();
177
178 unsafe {
179 ONCE.call_once(|| {
180 GLOBAL = MaybeUninit::new(Global {
181 value: AtomicUsize::new(0),
182 });
183 });
184 &*GLOBAL.as_ptr()
185 }
186 }
187 }
188
test_handler(_sig_num: c_int)189 extern "C" fn test_handler(_sig_num: c_int) {
190 let global = Global::get_instance();
191 global.value.fetch_add(1, Ordering::Relaxed);
192 }
193
194 /// SDV cases for signal register in multi-thread env
195 ///
196 /// # Brief
197 /// 1. Registers a signal handler
198 /// 2. Spawns another thread to raise the signal
199 /// 3. Raises the same signal on the main thread
200 /// 4. All execution should return OK
201 #[test]
sdv_signal_register_multi()202 fn sdv_signal_register_multi() {
203 for i in 0..1000 {
204 let res = unsafe {
205 ylong_signal::register_signal_action(libc::SIGCHLD, move || {
206 let mut data = 100;
207 data += i;
208 assert_eq!(data, 100 + i);
209 })
210 };
211 thread::spawn(move || {
212 unsafe { libc::raise(libc::SIGCHLD) };
213 });
214 assert!(res.is_ok());
215 unsafe {
216 libc::raise(libc::SIGCHLD);
217 }
218
219 ylong_signal::deregister_signal_action(libc::SIGCHLD);
220 unsafe {
221 libc::raise(libc::SIGCHLD);
222 }
223 assert!(res.is_ok());
224 }
225 }
226