Possible Atomicity Violation in poll_evented poll_ready
https://gitlab.redox-os.org/redox-os/tokio/blob/master/tokio-reactor/src/poll_evented.rs#L105-144
macro_rules! poll_ready {
($me:expr, $mask:expr, $cache:ident, $take:ident, $poll:expr) => {{
$me.register()?;
// Load cached & encoded readiness.
let mut cached = $me.inner.$cache.load(Relaxed); // 1
let mask = $mask | ::platform::hup();
// See if the current readiness matches any bits.
let mut ret = mio::Ready::from_usize(cached) & $mask;
if ret.is_empty() { // 2
...
} else {
// Check what's new with the registration stream. This will not
// request to be notified
if let Some(ready) = $me.inner.registration.$take()? {
cached |= ready.as_usize();
$me.inner.$cache.store(cached, Relaxed); // 3
}
Ok(mio::Ready::from_usize(cached).into()) // 4
}
}};
}
$cache is an Atomic Variable. In the buggy code:
- cache.load().
- check ret that is data dependent on 1.
- if not, update cached based on old value
- then return cached.
The function may be executed in the following patterns:
T1 | T2 |
---|---|
cached.load() | |
check ret (false) | |
cached.load() | |
check ret (false) | |
update cached based on old value | |
update cached based on new value |
But originally T1 should update cached on the old value.