Skip to content

Commit 62fd956

Browse files
committed
[Windows] CFThreadSpecific implementation allows access to deallocated value
Unlike pthread-based implementation, TLS/FLS on Windows doesn't return NULL when reading value after destructor call. To avoid that we have to nullify value in destructor callback. The implementation needs to store key along with user data to perform proper cleanup.
1 parent 9991f6a commit 62fd956

File tree

2 files changed

+41
-2
lines changed

2 files changed

+41
-2
lines changed

CoreFoundation/Base.subproj/CFPlatform.c

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,8 +1390,22 @@ CF_PRIVATE int asprintf(char **ret, const char *format, ...) {
13901390
extern void swift_retain(void *);
13911391
extern void swift_release(void *);
13921392

1393+
#if TARGET_OS_WIN32
1394+
typedef struct _CFThreadSpecificData {
1395+
CFTypeRef value;
1396+
_CFThreadSpecificKey key;
1397+
} _CFThreadSpecificData;
1398+
#endif
1399+
13931400
static void _CFThreadSpecificDestructor(void *ctx) {
1401+
#if TARGET_OS_WIN32
1402+
_CFThreadSpecificData *data = (_CFThreadSpecificData *)ctx;
1403+
FlsSetValue(data->key, NULL);
1404+
swift_release(data->value);
1405+
free(data);
1406+
#else
13941407
swift_release(ctx);
1408+
#endif
13951409
}
13961410

13971411
_CFThreadSpecificKey _CFThreadSpecificKeyCreate() {
@@ -1406,7 +1420,11 @@ _CFThreadSpecificKey _CFThreadSpecificKeyCreate() {
14061420

14071421
CFTypeRef _Nullable _CFThreadSpecificGet(_CFThreadSpecificKey key) {
14081422
#if TARGET_OS_WIN32
1409-
return (CFTypeRef)FlsGetValue(key);
1423+
_CFThreadSpecificData *data = (_CFThreadSpecificData *)FlsGetValue(key);
1424+
if (data == NULL) {
1425+
return NULL;
1426+
}
1427+
return data->value;
14101428
#else
14111429
return (CFTypeRef)pthread_getspecific(key);
14121430
#endif
@@ -1415,9 +1433,23 @@ CFTypeRef _Nullable _CFThreadSpecificGet(_CFThreadSpecificKey key) {
14151433
void _CFThreadSpecificSet(_CFThreadSpecificKey key, CFTypeRef _Nullable value) {
14161434
#if TARGET_OS_WIN32
14171435
if (value != NULL) {
1436+
_CFThreadSpecificData *oldData = (_CFThreadSpecificData *)FlsGetValue(key);
1437+
if (oldData) {
1438+
free(oldData);
1439+
}
1440+
1441+
_CFThreadSpecificData *data = malloc(sizeof(_CFThreadSpecificData));
1442+
if (!data) {
1443+
HALT_MSG("Out of memory");
1444+
}
1445+
data->value = value;
1446+
data->key = key;
1447+
14181448
swift_retain((void *)value);
1449+
FlsSetValue(key, data);
1450+
} else {
1451+
FlsSetValue(key, NULL);
14191452
}
1420-
FlsSetValue(key, value);
14211453
#else
14221454
if (value != NULL) {
14231455
swift_retain((void *)value);

Tests/Foundation/Tests/TestNotificationQueue.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,5 +229,12 @@ class TestNotificationQueue : XCTestCase {
229229
bgThread.start()
230230

231231
waitForExpectations(timeout: 0.2)
232+
233+
// There is a small time gap between "e.fulfill()"
234+
// and actuall thread termination.
235+
// We need a little delay to allow bgThread actually die.
236+
// Callers of this function are assuming thread is
237+
// deallocated after call.
238+
Thread.sleep(forTimeInterval: 0.05)
232239
}
233240
}

0 commit comments

Comments
 (0)