mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Fix struct_time field overflow to raise OverflowError in time module (#7627)
* Fix struct_time field overflow to raise OverflowError in time module * Address CodeRabbit review: cover tm_gmtoff and chain AssertionError * Fix ruff format: single space before inline comment
This commit is contained in:
1 parent
0b1b4f955e
commit
ebb3253458
2 files changed
+64
-54
No files matched your search
@@ -27,7 +27,7 @@ unsafe extern "C" {
|
||||
mod decl {
|
||||
use crate::{
|
||||
AsObject, Py, PyObjectRef, PyResult, VirtualMachine,
|
||||
builtins::{PyStrRef, PyTypeRef},
|
||||
builtins::{PyBaseExceptionRef, PyStrRef, PyTypeRef},
|
||||
function::{Either, FuncArgs, OptionalArg},
|
||||
types::{PyStructSequence, struct_sequence_new},
|
||||
};
|
||||
@@ -354,6 +354,13 @@ mod decl {
|
||||
) -> PyResult<CheckedTm> {
|
||||
let invalid_tuple =
|
||||
|| vm.new_type_error(format!("{func_name}(): illegal time tuple argument"));
|
||||
let classify_err = |e: PyBaseExceptionRef| {
|
||||
if e.class().is(vm.ctx.exceptions.overflow_error) {
|
||||
vm.new_overflow_error(format!("{func_name} argument out of range"))
|
||||
} else {
|
||||
invalid_tuple()
|
||||
}
|
||||
};
|
||||
|
||||
let year: i64 = t.tm_year.clone().try_into_value(vm).map_err(|e| {
|
||||
if e.class().is(vm.ctx.exceptions.overflow_error) {
|
||||
@@ -370,46 +377,30 @@ mod decl {
|
||||
.tm_mon
|
||||
.clone()
|
||||
.try_into_value::<i32>(vm)
|
||||
.map_err(|_| invalid_tuple())?
|
||||
.map_err(classify_err)?
|
||||
- 1;
|
||||
let tm_mday = t
|
||||
.tm_mday
|
||||
.clone()
|
||||
.try_into_value(vm)
|
||||
.map_err(|_| invalid_tuple())?;
|
||||
let tm_hour = t
|
||||
.tm_hour
|
||||
.clone()
|
||||
.try_into_value(vm)
|
||||
.map_err(|_| invalid_tuple())?;
|
||||
let tm_min = t
|
||||
.tm_min
|
||||
.clone()
|
||||
.try_into_value(vm)
|
||||
.map_err(|_| invalid_tuple())?;
|
||||
let tm_sec = t
|
||||
.tm_sec
|
||||
.clone()
|
||||
.try_into_value(vm)
|
||||
.map_err(|_| invalid_tuple())?;
|
||||
let tm_mday = t.tm_mday.clone().try_into_value(vm).map_err(classify_err)?;
|
||||
let tm_hour = t.tm_hour.clone().try_into_value(vm).map_err(classify_err)?;
|
||||
let tm_min = t.tm_min.clone().try_into_value(vm).map_err(classify_err)?;
|
||||
let tm_sec = t.tm_sec.clone().try_into_value(vm).map_err(classify_err)?;
|
||||
let tm_wday = (t
|
||||
.tm_wday
|
||||
.clone()
|
||||
.try_into_value::<i32>(vm)
|
||||
.map_err(|_| invalid_tuple())?
|
||||
.map_err(classify_err)?
|
||||
+ 1)
|
||||
% 7;
|
||||
let tm_yday = t
|
||||
.tm_yday
|
||||
.clone()
|
||||
.try_into_value::<i32>(vm)
|
||||
.map_err(|_| invalid_tuple())?
|
||||
.map_err(classify_err)?
|
||||
- 1;
|
||||
let tm_isdst = t
|
||||
.tm_isdst
|
||||
.clone()
|
||||
.try_into_value(vm)
|
||||
.map_err(|_| invalid_tuple())?;
|
||||
.map_err(classify_err)?;
|
||||
|
||||
let mut tm: libc::tm = unsafe { core::mem::zeroed() };
|
||||
tm.tm_year = year - 1900;
|
||||
@@ -474,7 +465,7 @@ mod decl {
|
||||
.tm_gmtoff
|
||||
.clone()
|
||||
.try_into_value(vm)
|
||||
.map_err(|_| invalid_tuple())?;
|
||||
.map_err(classify_err)?;
|
||||
tm.tm_gmtoff = gmtoff as _;
|
||||
}
|
||||
|
||||
@@ -963,41 +954,28 @@ mod decl {
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<libc::tm> {
|
||||
let invalid_tuple = || vm.new_type_error("mktime(): illegal time tuple argument");
|
||||
let year: i32 = t
|
||||
.tm_year
|
||||
.clone()
|
||||
.try_into_value(vm)
|
||||
.map_err(|_| invalid_tuple())?;
|
||||
let classify_err = |e: PyBaseExceptionRef| {
|
||||
if e.class().is(vm.ctx.exceptions.overflow_error) {
|
||||
vm.new_overflow_error("mktime argument out of range")
|
||||
} else {
|
||||
invalid_tuple()
|
||||
}
|
||||
};
|
||||
let year: i32 = t.tm_year.clone().try_into_value(vm).map_err(classify_err)?;
|
||||
if year < i32::MIN + 1900 {
|
||||
return Err(vm.new_overflow_error("year out of range"));
|
||||
}
|
||||
|
||||
let mut tm: libc::tm = unsafe { core::mem::zeroed() };
|
||||
tm.tm_sec = t
|
||||
.tm_sec
|
||||
.clone()
|
||||
.try_into_value(vm)
|
||||
.map_err(|_| invalid_tuple())?;
|
||||
tm.tm_min = t
|
||||
.tm_min
|
||||
.clone()
|
||||
.try_into_value(vm)
|
||||
.map_err(|_| invalid_tuple())?;
|
||||
tm.tm_hour = t
|
||||
.tm_hour
|
||||
.clone()
|
||||
.try_into_value(vm)
|
||||
.map_err(|_| invalid_tuple())?;
|
||||
tm.tm_mday = t
|
||||
.tm_mday
|
||||
.clone()
|
||||
.try_into_value(vm)
|
||||
.map_err(|_| invalid_tuple())?;
|
||||
tm.tm_sec = t.tm_sec.clone().try_into_value(vm).map_err(classify_err)?;
|
||||
tm.tm_min = t.tm_min.clone().try_into_value(vm).map_err(classify_err)?;
|
||||
tm.tm_hour = t.tm_hour.clone().try_into_value(vm).map_err(classify_err)?;
|
||||
tm.tm_mday = t.tm_mday.clone().try_into_value(vm).map_err(classify_err)?;
|
||||
tm.tm_mon = t
|
||||
.tm_mon
|
||||
.clone()
|
||||
.try_into_value::<i32>(vm)
|
||||
.map_err(|_| invalid_tuple())?
|
||||
.map_err(classify_err)?
|
||||
- 1;
|
||||
tm.tm_year = year - 1900;
|
||||
tm.tm_wday = -1;
|
||||
@@ -1005,13 +983,13 @@ mod decl {
|
||||
.tm_yday
|
||||
.clone()
|
||||
.try_into_value::<i32>(vm)
|
||||
.map_err(|_| invalid_tuple())?
|
||||
.map_err(classify_err)?
|
||||
- 1;
|
||||
tm.tm_isdst = t
|
||||
.tm_isdst
|
||||
.clone()
|
||||
.try_into_value(vm)
|
||||
.map_err(|_| invalid_tuple())?;
|
||||
.map_err(classify_err)?;
|
||||
Ok(tm)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,3 +17,35 @@ assert x2.tm_min == 16
|
||||
s = time.asctime(x)
|
||||
# print(s)
|
||||
assert s == "Thu Jan 1 00:16:40 1970"
|
||||
|
||||
|
||||
# Regression test for RustPython issue #4938:
|
||||
# struct_time field overflow should raise OverflowError (matching CPython),
|
||||
# not TypeError. Covers mktime, asctime, and strftime.
|
||||
I32_MAX_PLUS_1 = 2147483648
|
||||
overflow_cases = [
|
||||
(I32_MAX_PLUS_1, 1, 1, 0, 0, 0, 0, 0, 0), # i32 overflow in year
|
||||
(2024, I32_MAX_PLUS_1, 1, 0, 0, 0, 0, 0, 0), # i32 overflow in month
|
||||
(2024, 1, I32_MAX_PLUS_1, 0, 0, 0, 0, 0, 0), # i32 overflow in mday
|
||||
(2024, 1, 1, 0, 0, I32_MAX_PLUS_1, 0, 0, 0), # i32 overflow in sec
|
||||
(88888888888,) * 9, # multi-field i32 overflow
|
||||
]
|
||||
|
||||
for case in overflow_cases:
|
||||
for func_name, call in [
|
||||
("mktime", lambda c=case: time.mktime(c)),
|
||||
("asctime", lambda c=case: time.asctime(c)),
|
||||
("strftime", lambda c=case: time.strftime("%Y", c)),
|
||||
]:
|
||||
try:
|
||||
call()
|
||||
except OverflowError:
|
||||
pass # expected, matches CPython
|
||||
except TypeError as e:
|
||||
raise AssertionError(
|
||||
f"{func_name}({case}) raised TypeError (should be OverflowError): {e}"
|
||||
) from e
|
||||
else:
|
||||
raise AssertionError(
|
||||
f"{func_name}({case}) did not raise — expected OverflowError"
|
||||
)
|
||||
Reference in new issue
Block a user