Skip to content

<chrono>: steady_clock potentially overflows #656

@AlexGuteniev

Description

@AlexGuteniev

Describe the bug

STL/stl/inc/chrono

Lines 616 to 618 in a83d8c0

const long long _Whole = (_Ctr / _Freq) * period::den;
const long long _Part = (_Ctr % _Freq) * period::den / _Freq;
return time_point(duration(_Whole + _Part));

Line 617 causes overflow if _Ctr * period::den overflows 64 bit signed maximum, and _Freq is at least one greater than _Ctr.

Resulting _Freq that causes overflow is only 922 times greater than _Freq observed on my system. Future Windows version or future hardware may cause overflow.

Command-line test case

C:\Temp>type repro.cpp
#include <iostream>
#include <chrono>
#include <atomic>
#include <limits>


int main() {
    constexpr long long max_itermediate_value = (std::numeric_limits<long long>::max)();
    constexpr long long _Freq_max = max_itermediate_value / std::chrono::steady_clock::period::den + 1;
    constexpr long long _Freq_ovr = _Freq_max + 1;
    constexpr long long _Ctr_ovr = _Freq_ovr - 1;
    constexpr long long _Ctr_pre_ovr = _Ctr_ovr - 1;

    std::cout << _Freq_max << " is maximum _Query_perf_frequency() value not causing overflow\n\n";

    std::cout << "Otherwise with _Query_perf_frequency()=" << _Freq_ovr << " and _Query_perf_counter()=" << _Ctr_ovr <<"\n\n";

    std::cout << "The result of 'const long long _Part  = (_Ctr % _Freq) * period::den / _Freq;'\n"
        " with _Ctr=" << _Ctr_pre_ovr << " and _Freq=" << _Freq_ovr << " is:\n";

    std::cout << (_Ctr_pre_ovr % _Freq_ovr) * std::chrono::steady_clock::period::den / _Freq_ovr << "\n";

    std::cout << "The result of 'const long long _Part  = (_Ctr % _Freq) * period::den / _Freq;' \n"
        " with _Ctr=" << _Ctr_ovr << " and _Freq=" << _Freq_ovr << " is:\n";

    std::cout << (_Ctr_ovr % _Freq_ovr) * std::chrono::steady_clock::period::den / _Freq_ovr << "\n\n";

    std::cout << _Query_perf_frequency() << " is the current frequency\n"
        << "It is only " << _Freq_ovr / _Query_perf_frequency() << " times away\n";
}

C:\Temp>cl /EHsc /W4  .\repro.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.26.28720.3 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

repro.cpp
.\repro.cpp(26): warning C4307: '*': signed integral constant overflow
Microsoft (R) Incremental Linker Version 14.26.28720.3
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:repro.exe
repro.obj

C:\Temp>repro
9223372037 is maximum _Query_perf_frequency() value not causing overflow

Otherwise with _Query_perf_frequency()=9223372038 and _Query_perf_counter()=9223372037

The result of 'const long long _Part  = (_Ctr % _Freq) * period::den / _Freq;'
 with _Ctr=9223372036 and _Freq=9223372038 is:
999999999
The result of 'const long long _Part  = (_Ctr % _Freq) * period::den / _Freq;'
 with _Ctr=9223372037 and _Freq=9223372038 is:
-999999999

10000000 is the current frequency
It is only 922 times away

Expected behavior
I expect some algorithm that is robust for any values of _Freq and _Ctr. Maybe _div128 / _mul128. Maybe floating point.

STL version

  • Option 1: Visual Studio version
Microsoft Visual Studio Professional 2019 Preview
Version 16.6.0 Preview 2.0

Additional context
I've tried to optimize the algorithm in PR #653 .
I assumed that the original code did not suffer from overflow, so changed would not suffer either.
But the overflow exists, and the proposed optimization changes overflow condition to a more complex but more likely condition.
So I closed that PR and reporting this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingwontfixThis will not be worked on

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions