Skip to content

Commit 93fd478

Browse files
authored
faulthandler: use _PyTime_t rather than double for timeout (python#4139)
Use the _PyTime_t type rather than double for the faulthandler timeout in dump_traceback_later(). This change should fix the following Coverity warning: CID 1420311: Incorrect expression (UNINTENDED_INTEGER_DIVISION) Dividing integer expressions "9223372036854775807LL" and "1000LL", and then converting the integer quotient to type "double". Any remainder, or fractional part of the quotient, is ignored. if ((timeout * 1e6) >= (double) PY_TIMEOUT_MAX) { The warning comes from (double)PY_TIMEOUT_MAX with: #define PY_TIMEOUT_MAX (PY_LLONG_MAX / 1000)
1 parent 7351f9e commit 93fd478

File tree

1 file changed

+31
-20
lines changed

1 file changed

+31
-20
lines changed

Modules/faulthandler.c

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -607,30 +607,33 @@ cancel_dump_traceback_later(void)
607607
}
608608
}
609609

610+
#define SEC_TO_US (1000 * 1000)
611+
610612
static char*
611-
format_timeout(double timeout)
613+
format_timeout(_PyTime_t us)
612614
{
613-
unsigned long us, sec, min, hour;
614-
double intpart, fracpart;
615+
unsigned long sec, min, hour;
615616
char buffer[100];
616617

617-
fracpart = modf(timeout, &intpart);
618-
sec = (unsigned long)intpart;
619-
us = (unsigned long)(fracpart * 1e6);
618+
/* the downcast is safe: the caller check that 0 < us <= LONG_MAX */
619+
sec = (unsigned long)(us / SEC_TO_US);
620+
us %= SEC_TO_US;
621+
620622
min = sec / 60;
621623
sec %= 60;
622624
hour = min / 60;
623625
min %= 60;
624626

625-
if (us != 0)
627+
if (us != 0) {
626628
PyOS_snprintf(buffer, sizeof(buffer),
627-
"Timeout (%lu:%02lu:%02lu.%06lu)!\n",
628-
hour, min, sec, us);
629-
else
629+
"Timeout (%lu:%02lu:%02lu.%06u)!\n",
630+
hour, min, sec, (unsigned int)us);
631+
}
632+
else {
630633
PyOS_snprintf(buffer, sizeof(buffer),
631634
"Timeout (%lu:%02lu:%02lu)!\n",
632635
hour, min, sec);
633-
636+
}
634637
return _PyMem_Strdup(buffer);
635638
}
636639

@@ -639,8 +642,8 @@ faulthandler_dump_traceback_later(PyObject *self,
639642
PyObject *args, PyObject *kwargs)
640643
{
641644
static char *kwlist[] = {"timeout", "repeat", "file", "exit", NULL};
642-
double timeout;
643-
PY_TIMEOUT_T timeout_us;
645+
PyObject *timeout_obj;
646+
_PyTime_t timeout, timeout_us;
644647
int repeat = 0;
645648
PyObject *file = NULL;
646649
int fd;
@@ -650,18 +653,25 @@ faulthandler_dump_traceback_later(PyObject *self,
650653
size_t header_len;
651654

652655
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
653-
"d|iOi:dump_traceback_later", kwlist,
654-
&timeout, &repeat, &file, &exit))
656+
"O|iOi:dump_traceback_later", kwlist,
657+
&timeout_obj, &repeat, &file, &exit))
655658
return NULL;
656-
if ((timeout * 1e6) >= (double) PY_TIMEOUT_MAX) {
657-
PyErr_SetString(PyExc_OverflowError, "timeout value is too large");
659+
660+
if (_PyTime_FromSecondsObject(&timeout, timeout_obj,
661+
_PyTime_ROUND_TIMEOUT) < 0) {
658662
return NULL;
659663
}
660-
timeout_us = (PY_TIMEOUT_T)(timeout * 1e6);
664+
timeout_us = _PyTime_AsMicroseconds(timeout, _PyTime_ROUND_TIMEOUT);
661665
if (timeout_us <= 0) {
662666
PyErr_SetString(PyExc_ValueError, "timeout must be greater than 0");
663667
return NULL;
664668
}
669+
/* Limit to LONG_MAX seconds for format_timeout() */
670+
if (timeout_us >= PY_TIMEOUT_MAX || timeout_us / SEC_TO_US >= LONG_MAX) {
671+
PyErr_SetString(PyExc_OverflowError,
672+
"timeout value is too large");
673+
return NULL;
674+
}
665675

666676
tstate = get_thread_state();
667677
if (tstate == NULL)
@@ -672,7 +682,7 @@ faulthandler_dump_traceback_later(PyObject *self,
672682
return NULL;
673683

674684
/* format the timeout */
675-
header = format_timeout(timeout);
685+
header = format_timeout(timeout_us);
676686
if (header == NULL)
677687
return PyErr_NoMemory();
678688
header_len = strlen(header);
@@ -683,7 +693,8 @@ faulthandler_dump_traceback_later(PyObject *self,
683693
Py_XINCREF(file);
684694
Py_XSETREF(thread.file, file);
685695
thread.fd = fd;
686-
thread.timeout_us = timeout_us;
696+
/* the downcast is safe: we check that 0 < timeout_us < PY_TIMEOUT_MAX */
697+
thread.timeout_us = (PY_TIMEOUT_T)timeout_us;
687698
thread.repeat = repeat;
688699
thread.interp = tstate->interp;
689700
thread.exit = exit;

0 commit comments

Comments
 (0)