@@ -42,6 +42,27 @@ _PyTime_overflow(void)
4242 "timestamp too large to convert to C _PyTime_t" );
4343}
4444
45+
46+ #if defined(MS_WINDOWS ) || defined(__APPLE__ )
47+ Py_LOCAL_INLINE (_PyTime_t )
48+ _PyTime_MulDiv (_PyTime_t ticks , _PyTime_t mul , _PyTime_t div )
49+ {
50+ _PyTime_t intpart , remaining ;
51+ /* Compute (ticks * mul / div) in two parts to prevent integer overflow:
52+ compute integer part, and then the remaining part.
53+
54+ (ticks * mul) / div == (ticks / div) * mul + (ticks % div) * mul / div
55+
56+ The caller must ensure that "(div - 1) * mul" cannot overflow. */
57+ intpart = ticks / div ;
58+ ticks %= div ;
59+ remaining = ticks * mul ;
60+ remaining /= div ;
61+ return intpart * mul + remaining ;
62+ }
63+ #endif /* defined(MS_WINDOWS) || defined(__APPLE__) */
64+
65+
4566time_t
4667_PyLong_AsTime_t (PyObject * obj )
4768{
@@ -700,29 +721,62 @@ pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
700721
701722#elif defined(__APPLE__ )
702723 static mach_timebase_info_data_t timebase ;
703- uint64_t time ;
724+ static uint64_t t0 = 0 ;
725+ uint64_t ticks ;
704726
705727 if (timebase .denom == 0 ) {
706728 /* According to the Technical Q&A QA1398, mach_timebase_info() cannot
707729 fail: https://developer.apple.com/library/mac/#qa/qa1398/ */
708730 (void )mach_timebase_info (& timebase );
709- }
710731
711- time = mach_absolute_time ();
732+ /* Sanity check: should never occur in practice */
733+ if (timebase .numer < 1 || timebase .denom < 1 ) {
734+ PyErr_SetString (PyExc_RuntimeError ,
735+ "invalid mach_timebase_info" );
736+ return -1 ;
737+ }
738+
739+ /* Check that timebase.numer and timebase.denom can be casted to
740+ _PyTime_t. In pratice, timebase uses uint32_t, so casting cannot
741+ overflow. At the end, only make sure that the type is uint32_t
742+ (_PyTime_t is 64-bit long). */
743+ assert (sizeof (timebase .numer ) < sizeof (_PyTime_t ));
744+ assert (sizeof (timebase .denom ) < sizeof (_PyTime_t ));
712745
713- /* apply timebase factor */
714- time *= timebase .numer ;
715- time /= timebase .denom ;
746+ /* Make sure that (ticks * timebase.numer) cannot overflow in
747+ _PyTime_MulDiv(), with ticks < timebase.denom.
716748
717- * tp = time ;
749+ Known time bases:
750+
751+ * always (1, 1) on Intel
752+ * (1000000000, 33333335) or (1000000000, 25000000) on PowerPC
753+
754+ None of these time bases can overflow with 64-bit _PyTime_t, but
755+ check for overflow, just in case. */
756+ if ((_PyTime_t )timebase .numer > _PyTime_MAX / (_PyTime_t )timebase .denom ) {
757+ PyErr_SetString (PyExc_OverflowError ,
758+ "mach_timebase_info is too large" );
759+ return -1 ;
760+ }
761+
762+ t0 = mach_absolute_time ();
763+ }
718764
719765 if (info ) {
720766 info -> implementation = "mach_absolute_time()" ;
721- info -> resolution = (double )timebase .numer / timebase .denom * 1e-9 ;
767+ info -> resolution = (double )timebase .numer / ( double ) timebase .denom * 1e-9 ;
722768 info -> monotonic = 1 ;
723769 info -> adjustable = 0 ;
724770 }
725771
772+ ticks = mach_absolute_time ();
773+ /* Use a "time zero" to reduce precision loss when converting time
774+ to floatting point number, as in time.monotonic(). */
775+ ticks -= t0 ;
776+ * tp = _PyTime_MulDiv (ticks ,
777+ (_PyTime_t )timebase .numer ,
778+ (_PyTime_t )timebase .denom );
779+
726780#elif defined(__hpux )
727781 hrtime_t time ;
728782
@@ -802,60 +856,93 @@ _PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
802856
803857#ifdef MS_WINDOWS
804858static int
805- win_perf_counter (double * tp , _Py_clock_info_t * info )
859+ win_perf_counter (_PyTime_t * tp , _Py_clock_info_t * info )
806860{
807- static LONGLONG cpu_frequency = 0 ;
808- static LONGLONG ctrStart ;
861+ static LONGLONG frequency = 0 ;
862+ static LONGLONG t0 = 0 ;
809863 LARGE_INTEGER now ;
810- double diff ;
864+ LONGLONG ticksll ;
865+ _PyTime_t ticks ;
811866
812- if (cpu_frequency == 0 ) {
867+ if (frequency == 0 ) {
813868 LARGE_INTEGER freq ;
814- QueryPerformanceCounter (& now );
815- ctrStart = now .QuadPart ;
816- if (!QueryPerformanceFrequency (& freq ) || freq .QuadPart == 0 ) {
869+ if (!QueryPerformanceFrequency (& freq )) {
817870 PyErr_SetFromWindowsErr (0 );
818871 return -1 ;
819872 }
820- cpu_frequency = freq .QuadPart ;
873+ frequency = freq .QuadPart ;
874+
875+ /* Sanity check: should never occur in practice */
876+ if (frequency < 1 ) {
877+ PyErr_SetString (PyExc_RuntimeError ,
878+ "invalid QueryPerformanceFrequency" );
879+ return -1 ;
880+ }
881+
882+ /* Check that frequency can be casted to _PyTime_t.
883+
884+ Make also sure that (ticks * SEC_TO_NS) cannot overflow in
885+ _PyTime_MulDiv(), with ticks < frequency.
886+
887+ Known QueryPerformanceFrequency() values:
888+
889+ * 10,000,000 (10 MHz): 100 ns resolution
890+ * 3,579,545 Hz (3.6 MHz): 279 ns resolution
891+
892+ None of these frequencies can overflow with 64-bit _PyTime_t, but
893+ check for overflow, just in case. */
894+ if (frequency > _PyTime_MAX
895+ || frequency > (LONGLONG )_PyTime_MAX / (LONGLONG )SEC_TO_NS ) {
896+ PyErr_SetString (PyExc_OverflowError ,
897+ "QueryPerformanceFrequency is too large" );
898+ return -1 ;
899+ }
900+
901+ QueryPerformanceCounter (& now );
902+ t0 = now .QuadPart ;
821903 }
822- QueryPerformanceCounter (& now );
823- diff = (double )(now .QuadPart - ctrStart );
904+
824905 if (info ) {
825906 info -> implementation = "QueryPerformanceCounter()" ;
826- info -> resolution = 1.0 / (double )cpu_frequency ;
907+ info -> resolution = 1.0 / (double )frequency ;
827908 info -> monotonic = 1 ;
828909 info -> adjustable = 0 ;
829910 }
830911
831- diff = diff / (double )cpu_frequency ;
832- * tp = diff ;
912+ QueryPerformanceCounter (& now );
913+ ticksll = now .QuadPart ;
914+
915+ /* Use a "time zero" to reduce precision loss when converting time
916+ to floatting point number, as in time.perf_counter(). */
917+ ticksll -= t0 ;
918+
919+ /* Make sure that casting LONGLONG to _PyTime_t cannot overflow,
920+ both types are signed */
921+ Py_BUILD_ASSERT (sizeof (ticksll ) <= sizeof (ticks ));
922+ ticks = (_PyTime_t )ticksll ;
923+
924+ * tp = _PyTime_MulDiv (ticks , SEC_TO_NS , (_PyTime_t )frequency );
833925 return 0 ;
834926}
835927#endif
836928
837929
838930int
839- _PyTime_GetPerfCounterDoubleWithInfo ( double * d , _Py_clock_info_t * info )
931+ _PyTime_GetPerfCounterWithInfo ( _PyTime_t * t , _Py_clock_info_t * info )
840932{
841933#ifdef MS_WINDOWS
842- return win_perf_counter (d , info );
934+ return win_perf_counter (t , info );
843935#else
844- _PyTime_t t ;
845- if (_PyTime_GetMonotonicClockWithInfo (& t , info ) < 0 ) {
846- return -1 ;
847- }
848- * d = _PyTime_AsSecondsDouble (t );
849- return 0 ;
936+ return _PyTime_GetMonotonicClockWithInfo (t , info );
850937#endif
851938}
852939
853940
854- double
855- _PyTime_GetPerfCounterDouble (void )
941+ _PyTime_t
942+ _PyTime_GetPerfCounter (void )
856943{
857- double t ;
858- if (_PyTime_GetPerfCounterDoubleWithInfo (& t , NULL )) {
944+ _PyTime_t t ;
945+ if (_PyTime_GetPerfCounterWithInfo (& t , NULL )) {
859946 Py_UNREACHABLE ();
860947 }
861948 return t ;
@@ -869,14 +956,13 @@ _PyTime_Init(void)
869956 are working properly to not have to check for exceptions at runtime. If
870957 a clock works once, it cannot fail in next calls. */
871958 _PyTime_t t ;
872- double d ;
873959 if (_PyTime_GetSystemClockWithInfo (& t , NULL ) < 0 ) {
874960 return -1 ;
875961 }
876962 if (_PyTime_GetMonotonicClockWithInfo (& t , NULL ) < 0 ) {
877963 return -1 ;
878964 }
879- if (_PyTime_GetPerfCounterDoubleWithInfo ( & d , NULL ) < 0 ) {
965+ if (_PyTime_GetPerfCounterWithInfo ( & t , NULL ) < 0 ) {
880966 return -1 ;
881967 }
882968 return 0 ;
0 commit comments