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//! Linux wrapping of signal syscall
15
16use std::{io, mem, ptr};
17
18use libc::{c_int, c_void, sigaction, siginfo_t};
19
20use crate::common::{SigAction, Signal};
21use crate::sig_map::SigMap;
22
23impl SigAction {
24    pub(crate) fn get_old_action(sig_num: c_int) -> io::Result<Self> {
25        let mut old_act: libc::sigaction = unsafe { mem::zeroed() };
26        unsafe {
27            if libc::sigaction(sig_num, ptr::null(), &mut old_act) != 0 {
28                return Err(io::Error::last_os_error());
29            }
30        }
31        Ok(SigAction {
32            sig_num,
33            act: old_act,
34        })
35    }
36}
37
38impl Signal {
39    pub(crate) fn replace_sigaction(sig_num: c_int, new_action: usize) -> io::Result<sigaction> {
40        let mut handler: libc::sigaction = unsafe { mem::zeroed() };
41        let mut old_act: libc::sigaction = unsafe { mem::zeroed() };
42
43        handler.sa_sigaction = new_action;
44        handler.sa_flags = libc::SA_RESTART | libc::SA_SIGINFO;
45
46        unsafe {
47            if libc::sigaction(sig_num, &handler, &mut old_act) != 0 {
48                return Err(io::Error::last_os_error());
49            }
50        }
51
52        Ok(old_act)
53    }
54}
55
56pub(crate) extern "C" fn sig_handler(sig_num: c_int, sig_info: *mut siginfo_t, data: *mut c_void) {
57    let sig_map = SigMap::get_instance();
58    let race_fallback = sig_map.race_old.read();
59    let signals = sig_map.data.read();
60
61    if let Some(signal) = signals.get(&sig_num) {
62        // sig_info should not be null, but in a sig handler we cannot panic directly,
63        // therefore we abort instead
64        if sig_info.is_null() {
65            unsafe { libc::abort() };
66        }
67
68        let info = unsafe { &*sig_info };
69        if let Some(act) = &signal.new_act {
70            act(info);
71        }
72    } else if let Some(fallback) = race_fallback.as_ref() {
73        // There could be a race condition between swapping the old handler with the new
74        // handler and storing the change back to the global during the register
75        // procedure. Because of the race condition, the old handler and the new
76        // action could both not get executed. In order to prevent this, we
77        // store the old handler into global before swapping the handler in
78        // register. And during the handler execution, if the the action
79        // of the signal cannot be found, we execute this old handler instead if the
80        // sig_num matches.
81        if fallback.sig_num == sig_num {
82            execute_act(&fallback.act, sig_num, sig_info, data);
83        }
84    }
85}
86
87fn execute_act(act: &sigaction, sig_num: c_int, sig_info: *mut siginfo_t, data: *mut c_void) {
88    let handler = act.sa_sigaction;
89
90    // SIG_DFL for the default action.
91    // SIG_IGN to ignore this signal.
92    if handler == libc::SIG_DFL || handler == libc::SIG_IGN {
93        return;
94    }
95
96    // If SA_SIGINFO flag is set, then the signal handler takes three arguments, not
97    // one. In this case, sa_sigaction should be set instead of sa_handler.
98    // We transmute the handler from ptr to actual function type according to
99    // definition.
100    if act.sa_flags & libc::SA_SIGINFO == 0 {
101        let action = unsafe { mem::transmute::<usize, extern "C" fn(c_int)>(handler) };
102        action(sig_num);
103    } else {
104        type Action = extern "C" fn(c_int, *mut siginfo_t, *mut c_void);
105        let action = unsafe { mem::transmute::<usize, Action>(handler) };
106        action(sig_num, sig_info, data);
107    }
108}
109