CFTimeZone: lock the timezone-cache lookup (data race)#64
Conversation
CFTimeZoneCreate looked up _kCFTimeZoneCache with CFDictionaryGetValue outside _kCFTimeZoneCacheLock, while the insert path adds entries -- which can rehash the dictionary -- under that lock. Concurrent CFTimeZoneCreate calls therefore raced: ThreadSanitizer reports the unlocked read in GSHashTableFindBucket (via CFTimeZoneCreate, CFTimeZone.c:155) against a concurrent GSHashTableCreateMutable / insert. The lazy cache creation was likewise double-checked with an unsynchronised pointer read. Create the cache and look up the entry under the lock. The insert path already re-checks under the lock, so the slow path is unchanged.
d5af6c0 to
c2f68fc
Compare
|
In this case, this optimization actually makes a little sense. The variable There is also no race condition here. Checking the variable against NULL happens twice, so there's never a chance that we will write to If other folks disagree, I'm OK with merging this PR. |
|
To clarify the race: it isn't the one-time creation of |
|
We don't call |
|
I'd rather accept this patch. Concurrent inserts or not, by the C standard this is a data race. On some architectures the store to EDIT: Also, correction for the other PR that had the double-checked lock: that one was also a data race for the same reason. |
Summary
CFTimeZoneCreatelooked up the global timezone cache without holding thecache lock:
while the insert path adds entries — which can rehash the dictionary — under
_kCFTimeZoneCacheLock. ConcurrentCFTimeZoneCreate/CFTimeZoneCreateWithNamecalls therefore race on the dictionary's internal bucket array, and the lazy
cache creation was double-checked with an unsynchronised pointer read.
Reproduction (ThreadSanitizer)
Built libgnustep-corebase with
-fsanitize=thread; 8 threads callCFTimeZoneCreateWithNamewith distinct zone names (abbreviation dictionarypre-warmed to isolate this race). TSan reports:
Fix
Create the cache and look up the entry under
_kCFTimeZoneCacheLock. Theinsert path already re-checks under the lock, so the slow path is unchanged.
After the change the
CFTimeZoneCreatecache race no longer appears under thesame TSan harness.