Skip to content

Commit eac20cc

Browse files
WillAydmroeschke
andauthored
Fix get_datetimestruct_days overflow (#56001)
* remove pandas/tests/frame UB * updated macro with filename / line num * fix typo * stay with PD_CHECK_OVERFLOW * updates * test fixes --------- Co-authored-by: Matthew Roeschke <[email protected]>
1 parent 5d0f9a8 commit eac20cc

File tree

1 file changed

+29
-19
lines changed

1 file changed

+29
-19
lines changed

pandas/_libs/src/vendored/numpy/datetime/np_datetime.c

+29-19
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,12 @@ This file is derived from NumPy 1.7. See NUMPY_LICENSE.txt
2020
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
2121
#endif // NPY_NO_DEPRECATED_API
2222

23-
#include <Python.h>
24-
2523
#include "pandas/vendored/numpy/datetime/np_datetime.h"
26-
2724
#define NO_IMPORT_ARRAY
2825
#define PY_ARRAY_UNIQUE_SYMBOL PANDAS_DATETIME_NUMPY
2926
#include <numpy/ndarrayobject.h>
3027
#include <numpy/npy_common.h>
28+
#include <stdbool.h>
3129

3230
#if defined(_WIN32)
3331
#ifndef ENABLE_INTSAFE_SIGNED_FUNCTIONS
@@ -58,12 +56,15 @@ _Static_assert(0, "__has_builtin not detected; please try a newer compiler");
5856
#endif
5957
#endif
6058

59+
#define XSTR(a) STR(a)
60+
#define STR(a) #a
61+
6162
#define PD_CHECK_OVERFLOW(FUNC) \
6263
do { \
6364
if ((FUNC) != 0) { \
6465
PyGILState_STATE gstate = PyGILState_Ensure(); \
6566
PyErr_SetString(PyExc_OverflowError, \
66-
"Overflow occurred in npy_datetimestruct_to_datetime"); \
67+
"Overflow occurred at " __FILE__ ":" XSTR(__LINE__)); \
6768
PyGILState_Release(gstate); \
6869
return -1; \
6970
} \
@@ -139,53 +140,53 @@ npy_int64 get_datetimestruct_days(const npy_datetimestruct *dts) {
139140
npy_int64 year, days = 0;
140141
const int *month_lengths;
141142

142-
year = dts->year - 1970;
143-
days = year * 365;
143+
PD_CHECK_OVERFLOW(checked_int64_sub(dts->year, 1970, &year));
144+
PD_CHECK_OVERFLOW(checked_int64_mul(year, 365, &days));
144145

145146
/* Adjust for leap years */
146147
if (days >= 0) {
147148
/*
148149
* 1968 is the closest leap year before 1970.
149150
* Exclude the current year, so add 1.
150151
*/
151-
year += 1;
152+
PD_CHECK_OVERFLOW(checked_int64_add(year, 1, &year));
152153
/* Add one day for each 4 years */
153-
days += year / 4;
154+
PD_CHECK_OVERFLOW(checked_int64_add(days, year / 4, &days));
154155
/* 1900 is the closest previous year divisible by 100 */
155-
year += 68;
156+
PD_CHECK_OVERFLOW(checked_int64_add(year, 68, &year));
156157
/* Subtract one day for each 100 years */
157-
days -= year / 100;
158+
PD_CHECK_OVERFLOW(checked_int64_sub(days, year / 100, &days));
158159
/* 1600 is the closest previous year divisible by 400 */
159-
year += 300;
160+
PD_CHECK_OVERFLOW(checked_int64_add(year, 300, &year));
160161
/* Add one day for each 400 years */
161-
days += year / 400;
162+
PD_CHECK_OVERFLOW(checked_int64_add(days, year / 400, &days));
162163
} else {
163164
/*
164165
* 1972 is the closest later year after 1970.
165166
* Include the current year, so subtract 2.
166167
*/
167-
year -= 2;
168+
PD_CHECK_OVERFLOW(checked_int64_sub(year, 2, &year));
168169
/* Subtract one day for each 4 years */
169-
days += year / 4;
170+
PD_CHECK_OVERFLOW(checked_int64_add(days, year / 4, &days));
170171
/* 2000 is the closest later year divisible by 100 */
171-
year -= 28;
172+
PD_CHECK_OVERFLOW(checked_int64_sub(year, 28, &year));
172173
/* Add one day for each 100 years */
173-
days -= year / 100;
174+
PD_CHECK_OVERFLOW(checked_int64_sub(days, year / 100, &days));
174175
/* 2000 is also the closest later year divisible by 400 */
175176
/* Subtract one day for each 400 years */
176-
days += year / 400;
177+
PD_CHECK_OVERFLOW(checked_int64_add(days, year / 400, &days));
177178
}
178179

179180
month_lengths = days_per_month_table[is_leapyear(dts->year)];
180181
month = dts->month - 1;
181182

182183
/* Add the months */
183184
for (i = 0; i < month; ++i) {
184-
days += month_lengths[i];
185+
PD_CHECK_OVERFLOW(checked_int64_add(days, month_lengths[i], &days));
185186
}
186187

187188
/* Add the days */
188-
days += dts->day - 1;
189+
PD_CHECK_OVERFLOW(checked_int64_add(days, dts->day - 1, &days));
189190

190191
return days;
191192
}
@@ -430,6 +431,15 @@ npy_datetime npy_datetimestruct_to_datetime(NPY_DATETIMEUNIT base,
430431
}
431432

432433
const int64_t days = get_datetimestruct_days(dts);
434+
if (days == -1) {
435+
PyGILState_STATE gstate = PyGILState_Ensure();
436+
bool did_error = PyErr_Occurred() == NULL ? false : true;
437+
PyGILState_Release(gstate);
438+
if (did_error) {
439+
return -1;
440+
}
441+
}
442+
433443
if (base == NPY_FR_D) {
434444
return days;
435445
}

0 commit comments

Comments
 (0)