Fix CancellationTokenRegistration.Unregister race condition (#309)
authorStephen Toub <stoub@microsoft.com>
Mon, 2 Dec 2019 21:45:40 +0000 (16:45 -0500)
committerGitHub <noreply@github.com>
Mon, 2 Dec 2019 21:45:40 +0000 (16:45 -0500)
commitd4ef0d16186bbfba2fd078ba235f8759fd1128d3
treee7384a7ef0cf5edec47584d5d09e626fe1c63ee7
parent4e4e6373f1f3d7aa9f22270e1f754f8d663d8b34
Fix CancellationTokenRegistration.Unregister race condition (#309)

There's a race condition that exists if multiple threads are accessing the same CancellationTokenRegistration field, with one Unregistering and zero'ing out the field while another Unregisters.  We shouldn't ever be in a situation where we have a non-null node and a 0 id, but due to struct tearing we can end up in that exact situation inside of Unregister (if the thread zero'ing out the struct succeeded in zero'ing out the id but we still read a valid node).  If the node was then already unregistered, it will contain 0 for its id, in which case we'll see that the ids match and assume that means the node is still in the list.  At that point we proceed to dereference nodes in the list and null ref.

The fix is simple: rather than just asserting that we'll never get 0, explicitly check for 0 and return if it is.
src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs