Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix race condition when concurrently reading and modifying header
This fixes a potential number of race conditions. The most problematic one is the following. If we are in the middle of inflating a header and concurrently reading another flag there's a potential problem. If we check the header status first and it's not inflated but then read the flags after it's inflated, the flags contain bogus information. We fix this by making sure we always first copy the header, then check inflation status and return this copy if it is not inflated. This means that the code can see slightly outdated information, but it can already handle that properly because this type of race could happen in other places too. We also change every modification of changing the flags to be atomic. This is necessary to prevent certain information from being lost. If we for example freeze an object while concurrently inflating the header, it could be that we set the frozen bit in the header after we already copied the flags into the inflated header. In that case setting the frozen status would be lost. This is not a huge problem, but the side effect is more problematic. Since we reuse the header space as the pointer to the inflated header, modifying a bit there would mean the inflated header would point at a totally different inflated header. This would mean the object would suddenly get a very different status. These changes are related to issue #1940. In this issue the behavior exposed itself as follows. The code resulted in a concurrent scenario where an object header was inflated while concurrently retrieving an instance variable. Because of this, the returned obj_type was the value of the bits inside the inflated header pointer: $2 = { <rubinius::ObjectHeader> = { header = { f = { inflated = 1, meaning = 0, obj_type = 217, zone = rubinius::UnspecifiedZone, age = 2, Forwarded = 1, Remember = 1, Marked = 1, InImmix = 0, Pinned = 0, Frozen = 0, Tainted = 1, Untrusted = 0, LockContended = 0, unused = 0, aux_word = 1 }, flags64 = 4312680137, all_flags = 0x1010e46c9 }, klass_ = 0x104a3ff90, ivars_ = 0x1a, __body__ = {0x0} }, <No data fields>} The value here is obj_type 217, which is actually an invalid value. What happens because of this is that the code in Object#get_ivar would see that obj_type. This resulted in it not recognizing it as a packed object and this not running the proper code to retrieve the instance variable. This resulted in a nil being returned as the instance variable instead of the actual instance variable and then resulted in a NoMethodError.
- Loading branch information
Showing
2 changed files
with
271 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.