mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Translate WASI signal dispositions through libc sentinels (#9024)
Keep Python disposition values distinct from WASI function-table pointers. Recognize wasi-libc SIG_ERR before updating Python handlers and preserve ignored/default/custom dispositions. Add synchronous signal and rejected-registration regressions to the existing snippet. AI assistance covered source investigation, implementation, and automated reference/baseline tests. Isolated patched runtime validation is pending. No human validation is claimed. Assisted-by: Codex:model-version-unavailable
This commit is contained in:
1 parent
bf984b465e
commit
d4b43b860d
3 files changed
+100
-24
No files matched your search
@@ -46,37 +46,63 @@ mod wasm {
|
||||
pub const SIGSEGV: i32 = 11;
|
||||
pub const SIGTERM: i32 = 15;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Handler {
|
||||
Default,
|
||||
Ignore,
|
||||
Custom(sighandler_t),
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
fn signal(signum: i32, handler: sighandler_t) -> sighandler_t;
|
||||
#[link_name = "signal"]
|
||||
fn c_signal(signum: i32, handler: sighandler_t) -> sighandler_t;
|
||||
#[link_name = "__SIG_IGN"]
|
||||
fn c_sig_ign(signum: i32);
|
||||
#[link_name = "__SIG_ERR"]
|
||||
fn c_sig_err(signum: i32);
|
||||
fn raise(signum: i32) -> i32;
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
fn as_raw(self) -> sighandler_t {
|
||||
match self {
|
||||
Self::Default => 0,
|
||||
Self::Ignore => c_sig_ign as *const () as sighandler_t,
|
||||
Self::Custom(handler) => handler,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_raw(handler: sighandler_t) -> io::Result<Self> {
|
||||
// wasi-libc uses function pointers for SIG_IGN and SIG_ERR because
|
||||
// table index 1 is also a valid custom Wasm function pointer.
|
||||
if handler == c_sig_err as *const () as sighandler_t {
|
||||
Err(io::Error::last_os_error())
|
||||
} else if handler == 0 {
|
||||
Ok(Self::Default)
|
||||
} else if handler == c_sig_ign as *const () as sighandler_t {
|
||||
Ok(Self::Ignore)
|
||||
} else {
|
||||
Ok(Self::Custom(handler))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure `signalnum` is a valid platform signal number.
|
||||
pub unsafe fn probe_handler(signalnum: i32) -> Option<sighandler_t> {
|
||||
let handler = unsafe { signal(signalnum, SIG_IGN) };
|
||||
if handler == SIG_ERR {
|
||||
None
|
||||
} else {
|
||||
unsafe { signal(signalnum, handler) };
|
||||
Some(handler)
|
||||
}
|
||||
pub unsafe fn probe_handler(signalnum: i32) -> Option<Handler> {
|
||||
let handler = unsafe { install_handler(signalnum, Handler::Ignore) }.ok()?;
|
||||
unsafe { install_handler(signalnum, handler) }.ok()?;
|
||||
Some(handler)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure `signalnum` is a valid platform signal number and
|
||||
/// `handler` is accepted by the platform signal ABI.
|
||||
pub unsafe fn install_handler(
|
||||
signalnum: i32,
|
||||
handler: sighandler_t,
|
||||
) -> io::Result<sighandler_t> {
|
||||
let old = unsafe { signal(signalnum, handler) };
|
||||
if old == SIG_ERR {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(old)
|
||||
/// a custom `handler` is accepted by the platform signal ABI.
|
||||
pub unsafe fn install_handler(signalnum: i32, handler: Handler) -> io::Result<Handler> {
|
||||
let old = unsafe { c_signal(signalnum, handler.as_raw()) };
|
||||
Handler::from_raw(old)
|
||||
}
|
||||
|
||||
pub fn raise_signal(signalnum: i32) -> io::Result<()> {
|
||||
|
||||
@@ -175,9 +175,16 @@ pub(crate) mod _signal {
|
||||
let Some(handler) = (unsafe { host_signal::probe_handler(signum) }) else {
|
||||
continue;
|
||||
};
|
||||
let py_handler = if handler == SIG_DFL {
|
||||
let (is_default, is_ignored) = cfg_select! {
|
||||
target_os = "wasi" => (
|
||||
handler == host_signal::Handler::Default,
|
||||
handler == host_signal::Handler::Ignore,
|
||||
),
|
||||
_ => (handler == SIG_DFL, handler == SIG_IGN),
|
||||
};
|
||||
let py_handler = if is_default {
|
||||
Some(sig_dfl.clone())
|
||||
} else if handler == SIG_IGN {
|
||||
} else if is_ignored {
|
||||
Some(sig_ign.clone())
|
||||
} else {
|
||||
None
|
||||
@@ -223,15 +230,29 @@ pub(crate) mod _signal {
|
||||
}
|
||||
|
||||
let sig_handler = if handler.is_callable() {
|
||||
run_signal as *const () as sighandler_t
|
||||
let callback = run_signal as *const () as sighandler_t;
|
||||
cfg_select! {
|
||||
target_os = "wasi" => host_signal::Handler::Custom(callback),
|
||||
_ => callback,
|
||||
}
|
||||
} else {
|
||||
const MSG: &str =
|
||||
"signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object";
|
||||
|
||||
usize::try_from_borrowed_object(vm, &handler)
|
||||
let disposition = usize::try_from_borrowed_object(vm, &handler)
|
||||
.ok()
|
||||
.filter(|&v| matches!(v, SIG_DFL | SIG_IGN))
|
||||
.ok_or_else(|| vm.new_type_error(MSG))?
|
||||
.ok_or_else(|| vm.new_type_error(MSG))?;
|
||||
cfg_select! {
|
||||
target_os = "wasi" => {
|
||||
if disposition == SIG_DFL {
|
||||
host_signal::Handler::Default
|
||||
} else {
|
||||
host_signal::Handler::Ignore
|
||||
}
|
||||
},
|
||||
_ => disposition,
|
||||
}
|
||||
};
|
||||
|
||||
signal::check_signals(vm)?;
|
||||
|
||||
@@ -21,6 +21,35 @@ assert old_signal is signal.SIG_IGN
|
||||
assert signal.getsignal(signal.SIGILL) is signal.SIG_DFL
|
||||
|
||||
|
||||
# Ignoring a synchronously raised signal must not invoke a callback, including
|
||||
# on WASI where libc uses a function pointer rather than integer 1 for SIG_IGN.
|
||||
synchronous_signals = []
|
||||
|
||||
|
||||
def synchronous_handler(signum, frame):
|
||||
synchronous_signals.append(signum)
|
||||
|
||||
|
||||
previous = signal.signal(signal.SIGILL, synchronous_handler)
|
||||
try:
|
||||
signal.raise_signal(signal.SIGILL)
|
||||
assert synchronous_signals == [signal.SIGILL]
|
||||
assert signal.signal(signal.SIGILL, signal.SIG_IGN) is synchronous_handler
|
||||
signal.raise_signal(signal.SIGILL)
|
||||
assert synchronous_signals == [signal.SIGILL]
|
||||
assert signal.getsignal(signal.SIGILL) is signal.SIG_IGN
|
||||
finally:
|
||||
signal.signal(signal.SIGILL, previous)
|
||||
|
||||
if sys.platform == "wasi":
|
||||
# wasi-libc rejects SIGKILL (9) and SIGSTOP (19), even though they are not
|
||||
# exported by RustPython's minimal WASI signal module.
|
||||
for uncatchable in (9, 19):
|
||||
previous = signal.getsignal(uncatchable)
|
||||
assert_raises(OSError, signal.signal, uncatchable, signal.SIG_IGN)
|
||||
assert signal.getsignal(uncatchable) is previous
|
||||
|
||||
|
||||
# unix, and not WASI
|
||||
if "win" not in sys.platform and sys.platform != "wasi":
|
||||
signal.signal(signal.SIGALRM, handler)
|
||||
|
||||
Reference in new issue
Block a user