sync: detect double lock from the same thread #19337

pull vasild wants to merge 2 commits into bitcoin:master from vasild:detect_double_lock changing 3 files +112 −11
  1. vasild commented at 1:49 pm on June 20, 2020: member

    Double lock of the same (non-recursive) mutex from the same thread would produce an undefined behavior. Detect this from DEBUG_LOCKORDER and react similarly to the deadlock detection.

    This came up during discussion in another, related PR: #19238 (review).

  2. hebasto commented at 1:54 pm on June 20, 2020: member
    Concept ACK.
  3. in src/sync.cpp:225 in 401d3b759e outdated
    214 }
    215+template void EnterCritical(const char*, const char*, int, Mutex*, bool);
    216+template void EnterCritical(const char*, const char*, int, RecursiveMutex*, bool);
    217+template void EnterCritical(const char*, const char*, int, std::mutex*, bool);
    218+template void EnterCritical(const char*, const char*, int, std::recursive_mutex*, bool);
    219+template void EnterCritical(const char*, const char*, int, boost::mutex*, bool);
    


    MarcoFalke commented at 1:54 pm on June 20, 2020:
    no need to add dead code or code that is only used in tests

    vasild commented at 2:10 pm on June 20, 2020:

    These instantiations are required even if tests do not exist. Without them it wouldn’t link with errors like:

    0ld: error: undefined symbol: void EnterCritical<boost::mutex>(char const*, char const*, int, boost::mutex*, bool)
    1>>> referenced by checkqueue.h:184 (src/checkqueue.h:184)
    2>>>               libbitcoin_server_a-validation.o:(CCheckQueueControl<CScriptCheck>::CCheckQueueControl(CCheckQueue<CScriptCheck>*)) in archive libbitcoin_server.a
    

    hebasto commented at 2:20 pm on June 20, 2020:

    Yeah, this is solved in #18710 :)

    EDIT: I mean boost::mutex


    vasild commented at 7:07 am on June 22, 2020:
    So when these two PRs meet and if that was the last usage of boost::mutex, then this line can be removed: template void EnterCritical(const char*, const char*, int, boost::mutex*, bool);.

    MarcoFalke commented at 2:03 pm on August 5, 2020:
    I tried compiling locally without it and it worked #19337 (comment)

    vasild commented at 8:51 am on January 26, 2021:

    Just for reference - the two PRs met in master and afterwards that line was removed in #21010.

    Thanks, @fanquake!

  4. MarcoFalke commented at 1:54 pm on June 20, 2020: member
    Concept ACK
  5. MarcoFalke added the label Refactoring on Jun 20, 2020
  6. MarcoFalke removed the label Refactoring on Jun 20, 2020
  7. MarcoFalke added the label Utils/log/libs on Jun 20, 2020
  8. hebasto commented at 2:53 pm on June 20, 2020: member
  9. hebasto commented at 3:08 pm on June 20, 2020: member

    Tested 401d3b759e14a96332a033485257d2e993c2b944 with the following diff:

     0--- a/src/addrman.h
     1+++ b/src/addrman.h
     2@@ -178,7 +178,7 @@ class CAddrMan
     3 friend class CAddrManTest;
     4 protected:
     5     //! critical section to protect the inner data structures
     6-    mutable RecursiveMutex cs;
     7+    mutable Mutex cs;
     8 
     9 private:
    10     //! last used nId
    
    0$ ./src/qt/bitcoin-qt -regtest
    1Assertion failed: detected double lock at sync.cpp:153, details in debug log.
    2Aborted (core dumped)
    

    Nice!

    Some non-blocking nits:

    • could place boost headers into a separate group
    • in commit 401d3b759e14a96332a033485257d2e993c2b944 message “… would produce an undefined behavior” – mind rewording “would” -> “is”
    • could use unnamed namespace instead of static
  10. hebasto commented at 3:33 pm on June 20, 2020: member

    Travis error:

    It is reliably reproducible locally.

  11. hebasto commented at 3:48 pm on June 20, 2020: member

    Travis error:

    It is reliably reproducible locally. @vasild idk exactly why but moving the potential_deadlock_detected test case to the end fixes the problem.

    UPDATE: at the end of the potential_deadlock_detected test case the lock stack is not empty.

  12. hebasto commented at 8:27 pm on June 20, 2020: member

    Travis error:

    * https://travis-ci.org/github/bitcoin/bitcoin/jobs/700351407#L4430
    

    Fixed in #19340.

  13. DrahtBot commented at 10:06 pm on June 20, 2020: member

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #19983 (Drop some TSan suppressions by hebasto)
    • #19982 (test: Fix inconsistent lock order in wallet_tests/CreateWalletFromFile by hebasto)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  14. jnewbery commented at 6:32 pm on June 21, 2020: member
    Concept ACK
  15. practicalswift commented at 9:00 am on June 22, 2020: contributor

    Concept ACK

    Nice!

  16. vasild force-pushed on Jun 22, 2020
  17. vasild commented at 10:17 am on June 22, 2020: member
    * could place boost headers into a separate group
    

    Done.

    * in commit [401d3b7](https://github.com/bitcoin/bitcoin/commit/401d3b759e14a96332a033485257d2e993c2b944) message "... would produce an undefined behavior" -- mind rewording "would" -> "is"
    

    Done.

    * could use unnamed namespace instead of `static`
    

    The rest of the functions in the file use static, so for consistency I also used static in the newly introduced function. Maybe replace all static with an unnamed namespace in sync.cpp? If yes, then that would go as a separate commit, or maybe even a separate PR.

    Reviewers: this PR exposed an existent deficiency which is fixed in #19340.

  18. hebasto approved
  19. hebasto commented at 10:22 am on June 22, 2020: member

    ACK d32d85590e695fbe3acab0ce9a5525572a9bfa77

    But #19340 should be considering for reviewing before this PR merging.

  20. vasild marked this as a draft on Jun 22, 2020
  21. vasild commented at 11:04 am on June 22, 2020: member
    I converted this PR to “draft” so that it does not get merged accidentally. CI tests passed this time! I guess the order in which the tests are executed matters. #19340 will fix that and should be merged before this PR.
  22. promag commented at 8:23 pm on June 22, 2020: member
    Concept ACK, change LGTM.
  23. hebasto commented at 3:06 pm on August 4, 2020: member

    @vasild

    I converted this PR to “draft” so that it does not get merged accidentally. CI tests passed this time! I guess the order in which the tests are executed matters. #19340 will fix that and should be merged before this PR.

    #19340 is merged :)

  24. vasild marked this as ready for review on Aug 5, 2020
  25. sync: make EnterCritical() & push_lock() type safe
    The functions `EnterCritical()` and `push_lock()` take a pointer to a
    mutex, but that pointer used to be of type `void*` because we use a few
    different types for mutexes. This `void*` argument was not type safe
    because somebody could have send a pointer to anything that is not a
    mutex. Furthermore it wouldn't allow to check whether the passed mutex
    is recursive or not.
    
    Thus, change the functions to templated ones so that we can implement
    stricter checks for non-recursive mutexes. This also simplifies the
    callers of `EnterCritical()`.
    4df6567e4c
  26. vasild force-pushed on Aug 5, 2020
  27. vasild commented at 7:51 am on August 5, 2020: member
    Reopened this PR now that #19340 is merged and rebased to resolve a conflict.
  28. in src/test/sync_tests.cpp:60 in c269e0a224 outdated
    55+                return strcmp(e.what(), "double lock detected") == 0;
    56+            });
    57+    } else {
    58+        BOOST_CHECK_NO_THROW(TestDoubleLock2(m));
    59+    }
    60+    LEAVE_CRITICAL_SECTION(m);
    


    hebasto commented at 8:09 am on August 5, 2020:
    0    LEAVE_CRITICAL_SECTION(m);
    1    BOOST_CHECK(LockStackEmpty());
    

    vasild commented at 8:20 am on August 5, 2020:
    Done!
  29. hebasto approved
  30. hebasto commented at 8:09 am on August 5, 2020: member
    ACK c269e0a22423294b81c7a60862bc20f6ccf80f1c, tested on Linux Mint 20 (x86_64).
  31. vasild force-pushed on Aug 5, 2020
  32. hebasto approved
  33. hebasto commented at 8:22 am on August 5, 2020: member
    re-ACK b1f94072a1cc3ca67ce13658bb981d03480ef38e
  34. in src/test/sync_tests.cpp:41 in b1f94072a1 outdated
    36+#ifdef DEBUG_LOCKORDER
    37+template <typename MutexType>
    38+void TestDoubleLock2(MutexType& m)
    39+{
    40+    ENTER_CRITICAL_SECTION(m);
    41+    LEAVE_CRITICAL_SECTION(m);
    


    MarcoFalke commented at 8:30 am on August 5, 2020:
    Couldn’t this use LOCK(m), since ENTER_CRITICAL_SECTION is mostly only used internally to the sync module and other code used LOCK

    vasild commented at 12:32 pm on August 5, 2020:

    LOCK(m) does not compile if m is std::mutex:

    0src/sync.h:132:59: error: no type named 'UniqueLock' in 'std::__1::mutex'
    1template <typename Mutex, typename Base = typename Mutex::UniqueLock>
    2                                          ~~~~~~~~~~~~~~~~^~~~~~~~~~
    

    MarcoFalke commented at 2:02 pm on August 5, 2020:
    Though, we never use LOCK or even ENTER_CRITICAL_SECTION on std::mutex, so what is the point of testing for that? #19337 (comment)
  35. in src/test/sync_tests.cpp:51 in b1f94072a1 outdated
    46+{
    47+    const bool prev = g_debug_lockorder_abort;
    48+    g_debug_lockorder_abort = false;
    49+
    50+    MutexType m;
    51+    ENTER_CRITICAL_SECTION(m);
    


    MarcoFalke commented at 8:31 am on August 5, 2020:
    0    {
    1        LOCK(m);
    

    Same here. Should be equivalent?


    vasild commented at 12:35 pm on August 5, 2020:
    Same as above - it does not compile.
  36. in src/test/sync_tests.cpp:53 in b1f94072a1 outdated
    48+    g_debug_lockorder_abort = false;
    49+
    50+    MutexType m;
    51+    ENTER_CRITICAL_SECTION(m);
    52+    if (should_throw) {
    53+        BOOST_CHECK_EXCEPTION(
    


    MarcoFalke commented at 8:32 am on August 5, 2020:
    Could limit the #ifdef DEBUG_LOCKORDER to just this one line/statement? Benefit would be that BOOST_CHECK_NO_THROW will be run for non-debug builds as well.

    vasild commented at 12:43 pm on August 5, 2020:

    Without DEBUG_LOCKORDER we would actually do the double lock and once executed then we are in “undefined behavior” state. We don’t want to do that because it may randomly crash the test executable, overwrite random portions of memory for the excitement of subsequent tests, etc.

    https://github.com/bitcoin/bitcoin/blob/b1f94072a1cc3ca67ce13658bb981d03480ef38e/src/test/sync_tests.cpp#L93-L95


    MarcoFalke commented at 2:02 pm on August 5, 2020:
    I compiled and tested locally with sanitizers and it passed. Am I missing something? #19337 (comment)
  37. in src/sync.cpp:156 in b1f94072a1 outdated
    152+    }
    153+    // We throw or abort() here, never actually locking the mutex. So remove the entry
    154+    // from the lock stack which we just added in push_lock(). Tests in sync_tests.cpp
    155+    // continue executing past the exception and we don't want one test to leave a
    156+    // bricked stack for a subsequent test that could execute in the same thread.
    157+    lock_stack.pop_back();
    


    MarcoFalke commented at 8:39 am on August 5, 2020:
    Would be nice if the potential_deadlock_detected and double_lock_detected helpers used the same style to pop_back the just added lock. (Either pass in a mutable reference or create a copy)

    vasild commented at 12:52 pm on August 5, 2020:
    Done - used the same as used when calling potential_deadlock_detected() in master - a copy.
  38. in src/sync.cpp:154 in b1f94072a1 outdated
    155+    // continue executing past the exception and we don't want one test to leave a
    156+    // bricked stack for a subsequent test that could execute in the same thread.
    157+    lock_stack.pop_back();
    158+    if (g_debug_lockorder_abort) {
    159+        tfm::format(std::cerr, "Assertion failed: detected double lock at %s:%i, details in debug log.\n", __FILE__, __LINE__);
    160+        abort();
    


    MarcoFalke commented at 8:41 am on August 5, 2020:
    0        std::abort();
    

    Should be the same symbol, but slightly clearer.


    promag commented at 9:14 am on August 5, 2020:
    There’s more cases of s/abort()/std::abort so a follow up to to do this is also fine.

    vasild commented at 12:53 pm on August 5, 2020:
    I am leaving it as is for consistency with potential_deadlock_detected() which uses abort(). As @promag says, a subsequent patch could change all occurrences.
  39. MarcoFalke approved
  40. MarcoFalke commented at 8:41 am on August 5, 2020: member
    ACK, only style and test nits. Feel free to ingore.
  41. in src/sync.cpp:187 in b1f94072a1 outdated
    186+            }
    187+            // It is not a recursive mutex and it appears in the stack two times:
    188+            // at position `j` and at the end (which we added just before this loop).
    189+            // Can't allow locking the same (non-recursive) mutex two times from the
    190+            // same thread as that results in an undefined behavior.
    191+            double_lock_detected(c, lock_stack);
    


    promag commented at 9:22 am on August 5, 2020:
    Add assert(false) after this? Or some other “unreachable” assertion. Below, after potential_deadlock_detected() there’s a comment, could also add the assertion there.

    vasild commented at 12:54 pm on August 5, 2020:
    I added a comment, like done for potential_deadlock_detected().
  42. in src/sync.cpp:171 in b1f94072a1 outdated
    175     LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
    176     lock_stack.emplace_back(c, locklocation);
    177-    for (const LockStackItem& i : lock_stack) {
    178-        if (i.first == c)
    179-            break;
    180+    for (size_t j = 0; j < lock_stack.size() - 1; ++j) {
    


    promag commented at 9:24 am on August 5, 2020:
    Could keep range loop? When lock_stack is changed the iteration stops.

    vasild commented at 1:01 pm on August 5, 2020:
    We need to iterate on all but the last element.

    promag commented at 1:06 pm on August 5, 2020:
    Right, for double_lock_detected the last element should be skipped. But now lines L187-200 don’t run for the last element.

    promag commented at 9:48 pm on August 5, 2020:
    Nevermind, the last element was already skipped with if (i.first == c) break;.
  43. vasild commented at 1:01 pm on August 5, 2020: member
    Addressed some review suggestions.
  44. vasild force-pushed on Aug 5, 2020
  45. MarcoFalke commented at 2:02 pm on August 5, 2020: member
      0diff --git a/src/sync.cpp b/src/sync.cpp
      1index d020b4e334..55cc47191f 100644
      2--- a/src/sync.cpp
      3+++ b/src/sync.cpp
      4@@ -220,9 +220,6 @@ void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexTyp
      5 }
      6 template void EnterCritical(const char*, const char*, int, Mutex*, bool);
      7 template void EnterCritical(const char*, const char*, int, RecursiveMutex*, bool);
      8-template void EnterCritical(const char*, const char*, int, std::mutex*, bool);
      9-template void EnterCritical(const char*, const char*, int, std::recursive_mutex*, bool);
     10-template void EnterCritical(const char*, const char*, int, boost::mutex*, bool);
     11 
     12 void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line)
     13 {
     14diff --git a/src/test/sync_tests.cpp b/src/test/sync_tests.cpp
     15index 2ecc78bbed..9a3c276ed2 100644
     16--- a/src/test/sync_tests.cpp
     17+++ b/src/test/sync_tests.cpp
     18@@ -33,37 +33,44 @@ void TestPotentialDeadLockDetected(MutexType& mutex1, MutexType& mutex2)
     19     #endif
     20 }
     21 
     22-#ifdef DEBUG_LOCKORDER
     23 template <typename MutexType>
     24 void TestDoubleLock2(MutexType& m)
     25 {
     26-    ENTER_CRITICAL_SECTION(m);
     27-    LEAVE_CRITICAL_SECTION(m);
     28+    LOCK(m);
     29 }
     30 
     31 template <typename MutexType>
     32 void TestDoubleLock(bool should_throw)
     33 {
     34+#ifdef DEBUG_LOCKORDER
     35     const bool prev = g_debug_lockorder_abort;
     36     g_debug_lockorder_abort = false;
     37+#endif /* DEBUG_LOCKORDER */
     38 
     39     MutexType m;
     40-    ENTER_CRITICAL_SECTION(m);
     41-    if (should_throw) {
     42-        BOOST_CHECK_EXCEPTION(
     43-            TestDoubleLock2(m), std::logic_error, [](const std::logic_error& e) {
     44-                return strcmp(e.what(), "double lock detected") == 0;
     45-            });
     46-    } else {
     47-        BOOST_CHECK_NO_THROW(TestDoubleLock2(m));
     48+    {
     49+        LOCK(m);
     50+        if (should_throw) {
     51+#ifdef DEBUG_LOCKORDER
     52+            /* Double lock would produce an undefined behavior. Thus, we only do that if
     53+         * DEBUG_LOCKORDER is activated to detect it. We don't want non-DEBUG_LOCKORDER
     54+         * build to produce tests that exhibit known undefined behavior. */
     55+            BOOST_CHECK_EXCEPTION(
     56+                TestDoubleLock2(m), std::logic_error, [](const std::logic_error& e) {
     57+                    return strcmp(e.what(), "double lock detected") == 0;
     58+                });
     59+#endif /* DEBUG_LOCKORDER */
     60+        } else {
     61+            BOOST_CHECK_NO_THROW(TestDoubleLock2(m));
     62+        }
     63     }
     64-    LEAVE_CRITICAL_SECTION(m);
     65 
     66     BOOST_CHECK(LockStackEmpty());
     67 
     68+#ifdef DEBUG_LOCKORDER
     69     g_debug_lockorder_abort = prev;
     70-}
     71 #endif /* DEBUG_LOCKORDER */
     72+}
     73 } // namespace
     74 
     75 BOOST_FIXTURE_TEST_SUITE(sync_tests, BasicTestingSetup)
     76@@ -90,34 +97,14 @@ BOOST_AUTO_TEST_CASE(potential_deadlock_detected)
     77     #endif
     78 }
     79 
     80-/* Double lock would produce an undefined behavior. Thus, we only do that if
     81- * DEBUG_LOCKORDER is activated to detect it. We don't want non-DEBUG_LOCKORDER
     82- * build to produce tests that exhibit known undefined behavior. */
     83-#ifdef DEBUG_LOCKORDER
     84 BOOST_AUTO_TEST_CASE(double_lock_mutex)
     85 {
     86     TestDoubleLock<Mutex>(true /* should throw */);
     87 }
     88 
     89-BOOST_AUTO_TEST_CASE(double_lock_std_mutex)
     90-{
     91-    TestDoubleLock<std::mutex>(true /* should throw */);
     92-}
     93-
     94-BOOST_AUTO_TEST_CASE(double_lock_boost_mutex)
     95-{
     96-    TestDoubleLock<boost::mutex>(true /* should throw */);
     97-}
     98-
     99 BOOST_AUTO_TEST_CASE(double_lock_recursive_mutex)
    100 {
    101     TestDoubleLock<RecursiveMutex>(false /* should not throw */);
    102 }
    103 
    104-BOOST_AUTO_TEST_CASE(double_lock_std_recursive_mutex)
    105-{
    106-    TestDoubleLock<std::recursive_mutex>(false /* should not throw */);
    107-}
    108-#endif /* DEBUG_LOCKORDER */
    109-
    110 BOOST_AUTO_TEST_SUITE_END()
    
  46. vasild commented at 12:21 pm on August 6, 2020: member

    @MarcoFalke the above patch does not compile for me (neither with clang 11 nor with gcc 10) with an error like

    0ld: error: undefined symbol: void EnterCritical<std::mutex>(char const*, char const*, int, std::mutex*, bool)
    

    I think you should also get it for ./configure --enable-debug when the explicit instantiation of EnterCritical<std::mutex>() is removed. Here is how we end up needing that instantiation even though we never use std::mutex directly:

    0typedef AnnotatedMixin<std::mutex> Mutex;
    1
    2template <typename PARENT>
    3class LOCKABLE AnnotatedMixin : public PARENT
    4{
    5...
    6    using UniqueLock = std::unique_lock<PARENT>;
    

    So when one defines Mutex m; this ends up being AnnotatedMixin<std::mutex> m; and AnnotatedMixin<std::mutex>::UniqueLock is std::unique_lock<std::mutex>.

    LOCK(m) expands to DebugLock<AnnotatedMixin<std::mutex>> criticalblock123(m, ...) which expands to UniqueLock<AnnotatedMixin<std::mutex>> criticalblock123(m, ...) which expands to (with default 2nd template arg): UniqueLock<AnnotatedMixin<std::mutex>, AnnotatedMixin<std::mutex>::UniqueLock> criticalblock123(m, ...) which expands to UniqueLock<AnnotatedMixin<std::mutex>, std::unique_lock<std::mutex>> criticalblock123(m, ...).

    Then the constructor of the above calls the Enter() method which calls EnterCritical(name, file, line, Base::mutex()) which expands to EnterCritical(name, file, line, std::unique_lock<std::mutex>::mutex()). The mutex() method (4th argument) returns a pointer to std::mutex.

    This is how we end up calling EnterCritical() with a 4th argument of type pointer to std::mutex.

    Thus we need the explicit instantiation template void EnterCritical(const char*, const char*, int, std::mutex*, bool);. Same applies to std::recursive_mutex and boost::mutex.

  47. vasild referenced this in commit 2d547b4f7c on Aug 6, 2020
  48. vasild commented at 12:59 pm on August 6, 2020: member
    I pushed the patch to my travis instance and it breaks the build, similarly to what I observe locally: https://travis-ci.org/github/vasild/bitcoin/jobs/715483824#L3800
  49. MarcoFalke commented at 8:29 am on August 7, 2020: member
    Obviously I didn’t compile with --enable-debug (shame!). Please disregard the sync.cpp patch, but the other hunks should compile?
  50. sync: detect double lock from the same thread
    Double lock of the same (non-recursive) mutex from the same thread
    is producing an undefined behavior. Detect this from DEBUG_LOCKORDER
    and react similarly to the deadlock detection.
    95975dd08d
  51. vasild force-pushed on Aug 10, 2020
  52. vasild commented at 4:43 pm on August 10, 2020: member

    @MarcoFalke, I looked carefully at the diff above and took some of it but not all. Here are the reasons:

    • Indeed we never do LOCK() or ENTER_CRITICAL_SECTION() on std::mutex or std::recursive_mutex directly, so no need to test for that. Thus, I removed the tests double_lock_std_mutex and double_lock_std_recursive_mutex.

    • However we do call ENTER_CRITICAL_SECTION() on boost::mutex. So, I left the test double_lock_boost_mutex. So, TestDoubleLock2() cannot use LOCK(m) because it does not compile.

    • I do not think it makes sense to do these tests for non-DEBUG_LOCKORDER build because they will decay to:

      • if should_throw == true
      0mutex lock
      1if (should_throw) {
      2    // nothing
      3}
      4mutex unlock
      
      • if should_throw == false
      0recursive mutex lock
      1recursive mutex lock
      2recursive mutex unlock
      3recursive mutex unlock
      

      (with none of the code from sync.cpp exercised)

  53. hebasto approved
  54. hebasto commented at 10:29 am on August 15, 2020: member
    re-ACK 95975dd08d8fdaaeaf28e0d06b861ce2748c17b6
  55. hebasto commented at 10:30 am on August 15, 2020: member
    @ajtowns @ryanofsky @practicalswift Mind looking into this?
  56. laanwj commented at 10:14 pm on November 23, 2020: member
    Given how much of the current work on mutexes is converting recursive mutexes to non-resursive ones, it would be nice to get this in.
  57. laanwj commented at 3:44 pm on November 25, 2020: member
    code review ACK 95975dd08d8fdaaeaf28e0d06b861ce2748c17b6
  58. laanwj merged this on Nov 25, 2020
  59. laanwj closed this on Nov 25, 2020

  60. vasild deleted the branch on Nov 25, 2020
  61. sidhujag referenced this in commit e7388e9c10 on Nov 25, 2020
  62. in src/sync.cpp:153 in 95975dd08d
    149+            LogPrintf(" (*)"); /* Continued */
    150+        }
    151+        LogPrintf(" %s\n", i.second.ToString());
    152+    }
    153+    if (g_debug_lockorder_abort) {
    154+        tfm::format(std::cerr, "Assertion failed: detected double lock at %s:%i, details in debug log.\n", __FILE__, __LINE__);
    


    MarcoFalke commented at 10:00 am on November 26, 2020:

    This will always blame the bug on sync.cpp itself:

    0detected double lock at sync.cpp:153
    

    vasild commented at 1:48 pm on November 26, 2020:
  63. MarcoFalke commented at 10:08 am on November 26, 2020: member
    review ack
  64. in src/sync.cpp:182 in 95975dd08d
    181+            // It is not a recursive mutex and it appears in the stack two times:
    182+            // at position `j` and at the end (which we added just before this loop).
    183+            // Can't allow locking the same (non-recursive) mutex two times from the
    184+            // same thread as that results in an undefined behavior.
    185+            auto lock_stack_copy = lock_stack;
    186+            lock_stack.pop_back();
    


    promag commented at 9:38 am on December 2, 2020:
    @vasild why copy and pop here?

    vasild commented at 5:57 am on December 3, 2020:
    0            auto lock_stack_copy = lock_stack;
    1            lock_stack.pop_back();
    2            double_lock_detected(c, lock_stack_copy);
    

    That’s subtle:

    1. double_lock_detected() can abort() in which case it does not matter, but it can also throw and tests catch this exception and continue working and executing other tests. So the lock stack needs to be correct after the throw because it is going to be reused by subsequent tests. Notice that we did not acquire the offending mutex that is at the top of the lock stack - we only detected a request to acquire it, which if executed would cause a double lock. So the topmost element needs to be removed before the throw.
    2. double_lock_detected() needs the full lock stack, including the offending-but-not-actually-acquired-mutex because it prints some of its elements. It will want to print that last one.
  65. fanquake referenced this in commit f827e151a2 on Jan 26, 2021
  66. MarcoFalke referenced this in commit 280d0bd0bd on Jan 26, 2021
  67. remyers referenced this in commit 3edbc4f0ad on Jan 26, 2021
  68. DrahtBot locked this on Aug 16, 2022

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-12-18 21:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me