• Aaryaman Sagar's avatar
    Make futex functions free functions instead of members · 4d234b99
    Aaryaman Sagar authored
    Summary:
    The current futex API required a reference to a futex object in order to
    invoke `futexWake()`, although this is not buggy by itself, it is technically
    UB and nothing is stopping ASAN from catching this sort of use-after-free and
    reporting it as an error.  Especially when the futex is represented as a
    pointer, requiring a dereference to reach a member function
    
    The bug can come up when you call `futexWake()` on a futex that has been
    destroyed, for example
    
    ```
    auto&& futex_ptr = std::atomic<Futex<>*>{nullptr};
    auto&& thread = std::thread{[&]() {
      auto&& futex = Futex<>{0};
      futex_ptr.store(&futex);
    
      while (futex.load(std::memory_order_relaxed) != 1) {
        futex.futexWait(0);
      }
    }};
    
    while (!futex_ptr.load()) {}
    futex_ptr.load()->store(1);
    futex_ptr.load()->futexWake(1);
    thread.join();
    ```
    
    Here immediately after the `store(1)`, our thread could have loaded the value,
    seen that it had changed, and never went to sleep.  Or it could have seen the
    value as 0, went to sleep and immediately returned when it saw that the value
    in the futex word was not what was expected.
    
    In the scenario described above calling `futexWake()` is done on a "dangling"
    pointer.  To avoid this, we just never dereference the pointer, and pass the
    pointer to the futex syscall, where it will do the right things
    
    A side benefit to the refactor is that adding specializations is very easy.
    And we don't have to mess with member function specializations anymore, which
    are inherently hard to work with (eg. cannot partially specialize)
    
    The ADL extension points (currently implemented for `Futex<std::atomic>`,
    `Futex<DeterministicAtomic>` and `Futex<EmulatedFutexAtomic>`) are
    
    ```
    int futexWakeImpl(FutexType* futex, int count, uint32_t wakeMask);
    
    FutexResult futexWaitImpl(
        FutexType* futex,
        uint32_t expected,
        std::chrono::system_clock::time_point const* absSystemTime,
        std::chrono::steady_clock::time_point const* absSteadyTime,
        uint32_t waitMask);
    ```
    
    Reviewed By: yfeldblum
    
    Differential Revision: D9376527
    
    fbshipit-source-id: bb2b54e511fdf1da992c630a9bc7dc37f76da641
    4d234b99
SharedMutex.h 56.3 KB