Skip to content

Commit 240f87a

Browse files
authored
Handle negative time.sleep values (#5906)
* fix(time): Handle negative sleep values
1 parent 23a5c82 commit 240f87a

File tree

2 files changed

+44
-22
lines changed

2 files changed

+44
-22
lines changed

vm/src/convert/try_from.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::{
33
builtins::PyFloat,
44
object::{AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult},
55
};
6+
use malachite_bigint::Sign;
67
use num_traits::ToPrimitive;
78

89
/// Implemented by any type that can be created from a Python object.
@@ -124,10 +125,19 @@ impl<'a, T: PyPayload> TryFromBorrowedObject<'a> for &'a Py<T> {
124125
impl TryFromObject for std::time::Duration {
125126
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
126127
if let Some(float) = obj.payload::<PyFloat>() {
127-
Ok(Self::from_secs_f64(float.to_f64()))
128+
let f = float.to_f64();
129+
if f < 0.0 {
130+
return Err(vm.new_value_error("negative duration"));
131+
}
132+
Ok(Self::from_secs_f64(f))
128133
} else if let Some(int) = obj.try_index_opt(vm) {
129-
let sec = int?
130-
.as_bigint()
134+
let int = int?;
135+
let bigint = int.as_bigint();
136+
if bigint.sign() == Sign::Minus {
137+
return Err(vm.new_value_error("negative duration"));
138+
}
139+
140+
let sec = bigint
131141
.to_u64()
132142
.ok_or_else(|| vm.new_value_error("value out of range"))?;
133143
Ok(Self::from_secs(sec))

vm/src/stdlib/time.rs

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ unsafe extern "C" {
3434
#[pymodule(name = "time", with(platform))]
3535
mod decl {
3636
use crate::{
37-
PyObjectRef, PyResult, TryFromObject, VirtualMachine,
37+
AsObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine,
3838
builtins::{PyStrRef, PyTypeRef},
3939
function::{Either, FuncArgs, OptionalArg},
4040
types::PyStructSequence,
@@ -88,10 +88,37 @@ mod decl {
8888
duration_since_system_now(vm)
8989
}
9090

91-
#[cfg(not(unix))]
9291
#[pyfunction]
93-
fn sleep(dur: Duration) {
94-
std::thread::sleep(dur);
92+
fn sleep(seconds: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
93+
let dur = seconds.try_into_value::<Duration>(vm).map_err(|e| {
94+
if e.class().is(vm.ctx.exceptions.value_error) {
95+
if let Some(s) = e.args().first().and_then(|arg| arg.str(vm).ok()) {
96+
if s.as_str() == "negative duration" {
97+
return vm.new_value_error("sleep length must be non-negative");
98+
}
99+
}
100+
}
101+
e
102+
})?;
103+
104+
#[cfg(unix)]
105+
{
106+
// this is basically std::thread::sleep, but that catches interrupts and we don't want to;
107+
let ts = nix::sys::time::TimeSpec::from(dur);
108+
let res = unsafe { libc::nanosleep(ts.as_ref(), std::ptr::null_mut()) };
109+
let interrupted = res == -1 && nix::Error::last_raw() == libc::EINTR;
110+
111+
if interrupted {
112+
vm.check_signals()?;
113+
}
114+
}
115+
116+
#[cfg(not(unix))]
117+
{
118+
std::thread::sleep(dur);
119+
}
120+
121+
Ok(())
95122
}
96123

97124
#[cfg(not(target_os = "wasi"))]
@@ -690,21 +717,6 @@ mod platform {
690717
get_clock_time(ClockId::CLOCK_MONOTONIC, vm)
691718
}
692719

693-
#[pyfunction]
694-
fn sleep(dur: Duration, vm: &VirtualMachine) -> PyResult<()> {
695-
// this is basically std::thread::sleep, but that catches interrupts and we don't want to;
696-
697-
let ts = TimeSpec::from(dur);
698-
let res = unsafe { libc::nanosleep(ts.as_ref(), std::ptr::null_mut()) };
699-
let interrupted = res == -1 && nix::Error::last_raw() == libc::EINTR;
700-
701-
if interrupted {
702-
vm.check_signals()?;
703-
}
704-
705-
Ok(())
706-
}
707-
708720
#[cfg(not(any(
709721
target_os = "illumos",
710722
target_os = "netbsd",

0 commit comments

Comments
 (0)