LCOV - code coverage report
Current view: top level - src - timedata.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 24 52 46.2 %
Date: 2023-11-06 23:13:05 Functions: 4 6 66.7 %
Branches: 11 94 11.7 %

           Branch data     Line data    Source code
       1                 :            : // Copyright (c) 2014-2022 The Bitcoin Core developers
       2                 :            : // Distributed under the MIT software license, see the accompanying
       3                 :            : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4                 :            : 
       5                 :            : #if defined(HAVE_CONFIG_H)
       6                 :            : #include <config/bitcoin-config.h>
       7                 :            : #endif
       8                 :            : 
       9                 :            : #include <timedata.h>
      10                 :            : 
      11                 :            : #include <common/args.h>
      12                 :            : #include <logging.h>
      13                 :            : #include <netaddress.h>
      14                 :            : #include <node/interface_ui.h>
      15                 :            : #include <sync.h>
      16                 :            : #include <tinyformat.h>
      17                 :            : #include <util/translation.h>
      18                 :            : #include <warnings.h>
      19                 :            : 
      20                 :            : static GlobalMutex g_timeoffset_mutex;
      21                 :            : static int64_t nTimeOffset GUARDED_BY(g_timeoffset_mutex) = 0;
      22                 :            : 
      23                 :            : /**
      24                 :            :  * "Never go to sea with two chronometers; take one or three."
      25                 :            :  * Our three time sources are:
      26                 :            :  *  - System clock
      27                 :            :  *  - Median of other nodes clocks
      28                 :            :  *  - The user (asking the user to fix the system clock if the first two disagree)
      29                 :            :  */
      30                 :        812 : int64_t GetTimeOffset()
      31                 :            : {
      32                 :        812 :     LOCK(g_timeoffset_mutex);
      33                 :        812 :     return nTimeOffset;
      34                 :        812 : }
      35                 :            : 
      36                 :        812 : NodeClock::time_point GetAdjustedTime()
      37                 :            : {
      38                 :        812 :     return NodeClock::now() + std::chrono::seconds{GetTimeOffset()};
      39                 :            : }
      40                 :            : 
      41                 :            : #define BITCOIN_TIMEDATA_MAX_SAMPLES 200
      42                 :            : 
      43                 :          2 : static std::set<CNetAddr> g_sources;
      44                 :          2 : static CMedianFilter<int64_t> g_time_offsets{BITCOIN_TIMEDATA_MAX_SAMPLES, 0};
      45                 :            : static bool g_warning_emitted;
      46                 :            : 
      47                 :         15 : void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample)
      48                 :            : {
      49                 :         15 :     LOCK(g_timeoffset_mutex);
      50                 :            :     // Ignore duplicates
      51         [ -  + ]:         15 :     if (g_sources.size() == BITCOIN_TIMEDATA_MAX_SAMPLES)
      52                 :          0 :         return;
      53 [ +  - ][ +  + ]:         15 :     if (!g_sources.insert(ip).second)
      54                 :         14 :         return;
      55                 :            : 
      56                 :            :     // Add data
      57         [ +  - ]:          1 :     g_time_offsets.input(nOffsetSample);
      58 [ +  - ][ +  - ]:          1 :     LogPrint(BCLog::NET, "added time data, samples %d, offset %+d (%+d minutes)\n", g_time_offsets.size(), nOffsetSample, nOffsetSample / 60);
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
      59                 :            : 
      60                 :            :     // There is a known issue here (see issue #4521):
      61                 :            :     //
      62                 :            :     // - The structure g_time_offsets contains up to 200 elements, after which
      63                 :            :     // any new element added to it will not increase its size, replacing the
      64                 :            :     // oldest element.
      65                 :            :     //
      66                 :            :     // - The condition to update nTimeOffset includes checking whether the
      67                 :            :     // number of elements in g_time_offsets is odd, which will never happen after
      68                 :            :     // there are 200 elements.
      69                 :            :     //
      70                 :            :     // But in this case the 'bug' is protective against some attacks, and may
      71                 :            :     // actually explain why we've never seen attacks which manipulate the
      72                 :            :     // clock offset.
      73                 :            :     //
      74                 :            :     // So we should hold off on fixing this and clean it up as part of
      75                 :            :     // a timing cleanup that strengthens it in a number of other ways.
      76                 :            :     //
      77 [ +  - ][ -  + ]:          1 :     if (g_time_offsets.size() >= 5 && g_time_offsets.size() % 2 == 1) {
         [ #  # ][ #  # ]
      78         [ #  # ]:          0 :         int64_t nMedian = g_time_offsets.median();
      79         [ #  # ]:          0 :         std::vector<int64_t> vSorted = g_time_offsets.sorted();
      80                 :            :         // Only let other nodes change our time by so much
      81 [ #  # ][ #  # ]:          0 :         int64_t max_adjustment = std::max<int64_t>(0, gArgs.GetIntArg("-maxtimeadjustment", DEFAULT_MAX_TIME_ADJUSTMENT));
                 [ #  # ]
      82 [ #  # ][ #  # ]:          0 :         if (nMedian >= -max_adjustment && nMedian <= max_adjustment) {
      83                 :          0 :             nTimeOffset = nMedian;
      84                 :          0 :         } else {
      85                 :          0 :             nTimeOffset = 0;
      86                 :            : 
      87         [ #  # ]:          0 :             if (!g_warning_emitted) {
      88                 :            :                 // If nobody has a time different than ours but within 5 minutes of ours, give a warning
      89                 :          0 :                 bool fMatch = false;
      90         [ #  # ]:          0 :                 for (const int64_t nOffset : vSorted) {
      91 [ #  # ][ #  # ]:          0 :                     if (nOffset != 0 && nOffset > -5 * 60 && nOffset < 5 * 60) fMatch = true;
                 [ #  # ]
      92                 :            :                 }
      93                 :            : 
      94         [ #  # ]:          0 :                 if (!fMatch) {
      95                 :          0 :                     g_warning_emitted = true;
      96 [ #  # ][ #  # ]:          0 :                     bilingual_str strMessage = strprintf(_("Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly."), PACKAGE_NAME);
      97         [ #  # ]:          0 :                     SetMiscWarning(strMessage);
      98 [ #  # ][ #  # ]:          0 :                     uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING);
      99                 :          0 :                 }
     100                 :          0 :             }
     101                 :            :         }
     102                 :            : 
     103 [ #  # ][ #  # ]:          0 :         if (LogAcceptCategory(BCLog::NET, BCLog::Level::Debug)) {
     104         [ #  # ]:          0 :             std::string log_message{"time data samples: "};
     105         [ #  # ]:          0 :             for (const int64_t n : vSorted) {
     106 [ #  # ][ #  # ]:          0 :                 log_message += strprintf("%+d  ", n);
     107                 :            :             }
     108 [ #  # ][ #  # ]:          0 :             log_message += strprintf("|  median offset = %+d  (%+d minutes)", nTimeOffset, nTimeOffset / 60);
     109 [ #  # ][ #  # ]:          0 :             LogPrint(BCLog::NET, "%s\n", log_message);
         [ #  # ][ #  # ]
                 [ #  # ]
     110                 :          0 :         }
     111                 :          0 :     }
     112         [ -  + ]:         15 : }
     113                 :            : 
     114                 :          1 : void TestOnlyResetTimeData()
     115                 :            : {
     116                 :          1 :     LOCK(g_timeoffset_mutex);
     117                 :          1 :     nTimeOffset = 0;
     118                 :          1 :     g_sources.clear();
     119         [ +  - ]:          1 :     g_time_offsets = CMedianFilter<int64_t>{BITCOIN_TIMEDATA_MAX_SAMPLES, 0};
     120                 :          1 :     g_warning_emitted = false;
     121                 :          1 : }

Generated by: LCOV version 1.14