Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Avoid signed integer overflow in the :mod:`_thread` module.
17 changes: 10 additions & 7 deletions Modules/_threadmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,12 @@ static PyLockStatus
acquire_timed(PyThread_type_lock lock, _PyTime_t timeout)
{
PyLockStatus r;
_PyTime_t endtime = 0;
_PyTime_t starttime = 0;
_PyTime_t microseconds;

if (timeout > 0)
endtime = _PyTime_GetMonotonicClock() + timeout;
if (timeout > 0) {
starttime = _PyTime_GetMonotonicClock();
}

do {
microseconds = _PyTime_AsMicroseconds(timeout, _PyTime_ROUND_CEILING);
Expand All @@ -73,13 +74,15 @@ acquire_timed(PyThread_type_lock lock, _PyTime_t timeout)
/* If we're using a timeout, recompute the timeout after processing
* signals, since those can take time. */
if (timeout > 0) {
timeout = endtime - _PyTime_GetMonotonicClock();
_PyTime_t elapsed = _PyTime_GetMonotonicClock() - starttime;

/* Check for negative values, since those mean block forever.
*/
if (timeout < 0) {
if (timeout < elapsed) {
r = PY_LOCK_FAILURE;
}
else {
timeout -= elapsed;
starttime += elapsed;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that this part is really needed. I would prefer to leave starttime and timeout unchanged.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @vstinner, for your review.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original code also updates timeout every time it is interrupted. If you don’t adjust the timeout, I think the overall timeout could be too long. I expect you would be able to test this by installing a Python signal handler and sending signals while acquire or whatever method is running.

Perhaps you would prefer to calculate (overall) timeout − (overall) elapsed and pass that to PyThread_acquire_lock_timed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

starttime += elapsed will cause starttime overflow. Because starttime is closer to _PyTime_GetMonotonicClock() + timeout by accumulating, and _PyTime_GetMonotonicClock() + timeout may overflow.

I think the else block is not needed.

}
}
}
} while (r == PY_LOCK_INTR); /* Retry if we were interrupted. */
Expand Down