mirror of
https://git.wownero.com/wownero/wownero.git
synced 2024-12-22 18:18:52 +00:00
simplewallet: plug a timing leak
As reported by Tramèr et al, timing of refresh requests can be used to see whether a password was requested (and thus at least one output received) since this will induce a delay in subsequent calls. To avoid this, we schedule calls at a given time instead of sleeping for a set time (which would make delays additive). To further avoid a scheduled call being during the time in which a password is prompted, the actual scheduled time is now randomized.
This commit is contained in:
parent
dcff02e4c3
commit
38f6910481
@ -8807,12 +8807,25 @@ void simple_wallet::check_for_messages()
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void simple_wallet::wallet_idle_thread()
|
||||
{
|
||||
const boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::universal_time();
|
||||
while (true)
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(m_idle_mutex);
|
||||
if (!m_idle_run.load(std::memory_order_relaxed))
|
||||
break;
|
||||
|
||||
// if another thread was busy (ie, a foreground refresh thread), we'll end up here at
|
||||
// some random time that's not what we slept for, so we should not call refresh now
|
||||
// or we'll be leaking that fact through timing
|
||||
const boost::posix_time::ptime now0 = boost::posix_time::microsec_clock::universal_time();
|
||||
const uint64_t dt_actual = (now0 - start_time).total_microseconds() % 1000000;
|
||||
#ifdef _WIN32
|
||||
static const uint64_t threshold = 10000;
|
||||
#else
|
||||
static const uint64_t threshold = 2000;
|
||||
#endif
|
||||
if (dt_actual < threshold) // if less than a threshold... would a very slow machine always miss it ?
|
||||
{
|
||||
#ifndef _WIN32
|
||||
m_inactivity_checker.do_call(boost::bind(&simple_wallet::check_inactivity, this));
|
||||
#endif
|
||||
@ -8822,7 +8835,13 @@ void simple_wallet::wallet_idle_thread()
|
||||
|
||||
if (!m_idle_run.load(std::memory_order_relaxed))
|
||||
break;
|
||||
m_idle_cond.wait_for(lock, boost::chrono::seconds(1));
|
||||
}
|
||||
|
||||
// aim for the next multiple of 1 second
|
||||
const boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
|
||||
const auto dt = (now - start_time).total_microseconds();
|
||||
const auto wait = 1000000 - dt % 1000000;
|
||||
m_idle_cond.wait_for(lock, boost::chrono::microseconds(wait));
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
@ -448,10 +448,12 @@ namespace cryptonote
|
||||
std::atomic<bool> m_locked;
|
||||
std::atomic<bool> m_in_command;
|
||||
|
||||
template<uint64_t mini, uint64_t maxi> struct get_random_interval { public: uint64_t operator()() const { return crypto::rand_range(mini, maxi); } };
|
||||
|
||||
epee::math_helper::once_a_time_seconds<1> m_inactivity_checker;
|
||||
epee::math_helper::once_a_time_seconds<90> m_refresh_checker;
|
||||
epee::math_helper::once_a_time_seconds<90> m_mms_checker;
|
||||
epee::math_helper::once_a_time_seconds<90> m_rpc_payment_checker;
|
||||
epee::math_helper::once_a_time_seconds_range<get_random_interval<80 * 1000000, 100 * 1000000>> m_refresh_checker;
|
||||
epee::math_helper::once_a_time_seconds_range<get_random_interval<90 * 1000000, 110 * 1000000>> m_mms_checker;
|
||||
epee::math_helper::once_a_time_seconds_range<get_random_interval<90 * 1000000, 115 * 1000000>> m_rpc_payment_checker;
|
||||
|
||||
std::atomic<bool> m_need_payment;
|
||||
boost::posix_time::ptime m_last_rpc_payment_mining_time;
|
||||
|
Loading…
Reference in New Issue
Block a user