Skip to content

Commit 7e0f327

Browse files
Pull in main
2 parents cf1eb47 + 2713631 commit 7e0f327

5 files changed

Lines changed: 61 additions & 41 deletions

File tree

.github/workflows/build.yml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,19 @@ jobs:
157157
PYTHONSTRICTEXTENSIONBUILD: 1
158158
steps:
159159
- uses: actions/checkout@v3
160-
- name: Prepare homebrew environment variables
160+
- name: Install Homebrew dependencies
161+
run: brew install pkg-config openssl@1.1 xz gdbm tcl-tk
162+
- name: Prepare Homebrew environment variables
161163
run: |
162-
echo "LDFLAGS=-L$(brew --prefix tcl-tk)/lib" >> $GITHUB_ENV
164+
echo "CFLAGS=-I$(brew --prefix gdbm)/include -I$(brew --prefix xz)/include" >> $GITHUB_ENV
165+
echo "LDFLAGS=-L$(brew --prefix gdbm)/lib -I$(brew --prefix xz)/lib" >> $GITHUB_ENV
163166
echo "PKG_CONFIG_PATH=$(brew --prefix openssl@1.1)/lib/pkgconfig:$(brew --prefix tcl-tk)/lib/pkgconfig" >> $GITHUB_ENV
164167
- name: Configure CPython
165-
run: ./configure --with-pydebug --prefix=/opt/python-dev
168+
run: |
169+
./configure \
170+
--with-pydebug \
171+
--prefix=/opt/python-dev \
172+
--with-openssl="$(brew --prefix openssl@1.1)"
166173
- name: Build CPython
167174
run: make -j4
168175
- name: Display build info

Doc/c-api/exceptions.rst

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -402,51 +402,36 @@ Querying the error indicator
402402
403403
.. c:function:: PyObject *PyErr_GetRaisedException(void)
404404
405-
Returns the exception currently being raised, clearing the exception at
406-
the same time. Do not confuse this with the exception currently being
407-
handled which can be accessed with :c:func:`PyErr_GetHandledException`.
405+
Return the exception currently being raised, clearing the error indicator at
406+
the same time.
408407
409-
.. note::
408+
This function is used by code that needs to catch exceptions,
409+
or code that needs to save and restore the error indicator temporarily.
410410
411-
This function is normally only used by code that needs to catch exceptions or
412-
by code that needs to save and restore the error indicator temporarily, e.g.::
411+
For example::
413412
414-
{
415-
PyObject *exc = PyErr_GetRaisedException();
413+
{
414+
PyObject *exc = PyErr_GetRaisedException();
416415
417-
/* ... code that might produce other errors ... */
416+
/* ... code that might produce other errors ... */
418417
419-
PyErr_SetRaisedException(exc);
420-
}
418+
PyErr_SetRaisedException(exc);
419+
}
420+
421+
.. seealso:: :c:func:`PyErr_GetHandledException`,
422+
to save the exception currently being handled.
421423
422424
.. versionadded:: 3.12
423425
424426
425427
.. c:function:: void PyErr_SetRaisedException(PyObject *exc)
426428
427-
Sets the exception currently being raised ``exc``.
428-
If the exception is already set, it is cleared first.
429-
430-
``exc`` must be a valid exception.
431-
(Violating this rules will cause subtle problems later.)
432-
This call consumes a reference to the ``exc`` object: you must own a
433-
reference to that object before the call and after the call you no longer own
434-
that reference.
435-
(If you don't understand this, don't use this function. I warned you.)
429+
Set *exc* as the exception currently being raised,
430+
clearing the existing exception if one is set.
436431
437-
.. note::
438-
439-
This function is normally only used by code that needs to save and restore the
440-
error indicator temporarily. Use :c:func:`PyErr_GetRaisedException` to save
441-
the current exception, e.g.::
442-
443-
{
444-
PyObject *exc = PyErr_GetRaisedException();
445-
446-
/* ... code that might produce other errors ... */
432+
.. warning::
447433
448-
PyErr_SetRaisedException(exc);
449-
}
434+
This call steals a reference to *exc*, which must be a valid exception.
450435
451436
.. versionadded:: 3.12
452437

Doc/data/refcounts.dat

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,9 @@ PyErr_GetExcInfo:PyObject**:ptype:+1:
606606
PyErr_GetExcInfo:PyObject**:pvalue:+1:
607607
PyErr_GetExcInfo:PyObject**:ptraceback:+1:
608608

609+
PyErr_GetRaisedException:PyObject*::+1:
610+
PyErr_SetRaisedException::::
611+
609612
PyErr_GivenExceptionMatches:int:::
610613
PyErr_GivenExceptionMatches:PyObject*:given:0:
611614
PyErr_GivenExceptionMatches:PyObject*:exc:0:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix potential undefined behaviour in corner cases of floating-point-to-time
2+
conversions.

Python/pytime.c

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include "Python.h"
2-
#include "pycore_pymath.h" // _Py_InIntegralTypeRange()
32
#ifdef MS_WINDOWS
43
# include <winsock2.h> // struct timeval
54
#endif
@@ -41,6 +40,14 @@
4140
# error "unsupported time_t size"
4241
#endif
4342

43+
#if PY_TIME_T_MAX + PY_TIME_T_MIN != -1
44+
# error "time_t is not a two's complement integer type"
45+
#endif
46+
47+
#if _PyTime_MIN + _PyTime_MAX != -1
48+
# error "_PyTime_t is not a two's complement integer type"
49+
#endif
50+
4451

4552
static void
4653
pytime_time_t_overflow(void)
@@ -294,7 +301,21 @@ pytime_double_to_denominator(double d, time_t *sec, long *numerator,
294301
}
295302
assert(0.0 <= floatpart && floatpart < denominator);
296303

297-
if (!_Py_InIntegralTypeRange(time_t, intpart)) {
304+
/*
305+
Conversion of an out-of-range value to time_t gives undefined behaviour
306+
(C99 §6.3.1.4p1), so we must guard against it. However, checking that
307+
`intpart` is in range is delicate: the obvious expression `intpart <=
308+
PY_TIME_T_MAX` will first convert the value `PY_TIME_T_MAX` to a double,
309+
potentially changing its value and leading to us failing to catch some
310+
UB-inducing values. The code below works correctly under the mild
311+
assumption that time_t is a two's complement integer type with no trap
312+
representation, and that `PY_TIME_T_MIN` is within the representable
313+
range of a C double.
314+
315+
Note: we want the `if` condition below to be true for NaNs; therefore,
316+
resist any temptation to simplify by applying De Morgan's laws.
317+
*/
318+
if (!((double)PY_TIME_T_MIN <= intpart && intpart < -(double)PY_TIME_T_MIN)) {
298319
pytime_time_t_overflow();
299320
return -1;
300321
}
@@ -349,7 +370,8 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round)
349370
d = pytime_round(d, round);
350371
(void)modf(d, &intpart);
351372

352-
if (!_Py_InIntegralTypeRange(time_t, intpart)) {
373+
/* See comments in pytime_double_to_denominator */
374+
if (!((double)PY_TIME_T_MIN <= intpart && intpart < -(double)PY_TIME_T_MIN)) {
353375
pytime_time_t_overflow();
354376
return -1;
355377
}
@@ -515,8 +537,9 @@ pytime_from_double(_PyTime_t *tp, double value, _PyTime_round_t round,
515537
d *= (double)unit_to_ns;
516538
d = pytime_round(d, round);
517539

518-
if (!_Py_InIntegralTypeRange(_PyTime_t, d)) {
519-
pytime_overflow();
540+
/* See comments in pytime_double_to_denominator */
541+
if (!((double)_PyTime_MIN <= d && d < -(double)_PyTime_MIN)) {
542+
pytime_time_t_overflow();
520543
return -1;
521544
}
522545
_PyTime_t ns = (_PyTime_t)d;
@@ -910,7 +933,7 @@ py_get_system_clock(_PyTime_t *tp, _Py_clock_info_t *info, int raise_exc)
910933
info->monotonic = 0;
911934
info->adjustable = 1;
912935
if (clock_getres(CLOCK_REALTIME, &res) == 0) {
913-
info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
936+
info->resolution = (double)res.tv_sec + (double)res.tv_nsec * 1e-9;
914937
}
915938
else {
916939
info->resolution = 1e-9;

0 commit comments

Comments
 (0)