[fuchsia-async] Fix timer race

It was possible for timers to become indefinitely blocked, due to a bug
in the timer heap.

Consider two threads, one of which is in Timers::poll (T1) and the other
of which is in Timers::wake_timers_impl (T2):

T2: Remove the timer from the heap.
T2: Take the most recently registered waker.  There was none registered
    (perhaps because the timer expired before it was polled), so no task
    will wake.
T1: Register a waker.
T1: Load state; see that state is not FIRED, so return Poll::Pending.
    (Task is now blocking, waiting for someone to call `wake`)
T2: Store state to FIRED.
T2: Drop the timer.

Now, there is a waker registered, but the timer is no longer in the
heap, so `Waker::wake()` will never be called, which leads the task that
T1 was polling to block indefinitely.

This can be avoided by ensuring that T2 stores the FIRED state *before*
it takes the waker, which is the first half of this fix.  (Note that
`AtomicWaker::take` is a Release barrier so we can be certain that the
store of FIRED won't be ordered after it.)

However, that conflicts with the fast-path in `Timers::unregister`,
where we elide taking the lock on `inner` if the timer state is anything
but REGISTERED.  That is no longer safe, because if `unregister` (which
is called in Drop) exits without blocking on `inner`, then the timer's
resources could be freed, and then the other thread might access them,
which is a UAF.

I tested this fix with a reproducer that is reliable locally, but I've
chosen to not land the reproducer as it is very expensive and
high-level.

Bug: 392098305
Change-Id: I88af2097a9ec30304c7925a8efbb360a02a387a7
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/1203788
Commit-Queue: James Sullivan <jfsulliv@google.com>
Reviewed-by: Chris Suter <csuter@google.com>
1 file changed
tree: 930e24de56decdeaa3fef6170f12b2dfacde40f7
  1. .helix/
  2. boards/
  3. build/
  4. bundles/
  5. docs/
  6. examples/
  7. infra/
  8. products/
  9. scripts/
  10. sdk/
  11. src/
  12. third_party/
  13. tools/
  14. zircon/
  15. .clang-format
  16. .clang-tidy
  17. .editorconfig
  18. .git-blame-ignore-revs
  19. .gitattributes
  20. .gitignore
  21. .gitmodules
  22. .gn
  23. .ignore
  24. analysis_options.yaml
  25. AUTHORS
  26. BUILD.gn
  27. CODE_OF_CONDUCT.md
  28. CONTRIBUTING.md
  29. fuchsia.code-workspace
  30. LICENSE
  31. OWNERS
  32. PATENTS
  33. pyproject.toml
  34. pyrightconfig.json
  35. README.md
  36. rustfmt.toml
  37. shac.star
  38. shac.textproto
README.md

Fuchsia

What is Fuchsia?

Fuchsia is an open source, general purpose operating system supporting modern 64-bit Intel and ARM processors.

We expect everyone interacting with our project to respect our code of conduct.

Read more about Fuchsia's principles.

How can I build and run Fuchsia?

See Getting Started.

Where can I learn more about Fuchsia?

See fuchsia.dev.