Skip to content

Commit 511f6c9

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 ebf7f50 commit 511f6c9

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,18 +1420,36 @@ _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
14131431
}
14141432

14151433
void _CFThreadSpecificSet(_CFThreadSpecificKey key, CFTypeRef _Nullable value) {
1434+
// Intentionally not calling `swift_release` for previous value.
1435+
// OperationQueue uses these API (through NSThreadSpecific), and balances
1436+
// retain count manually.
14161437
#if TARGET_OS_WIN32
1438+
free(FlsGetValue(key));
1439+
1440+
_CFThreadSpecificData *data = NULL;
14171441
if (value != NULL) {
1442+
data = malloc(sizeof(_CFThreadSpecificData));
1443+
if (!data) {
1444+
HALT_MSG("Out of memory");
1445+
}
1446+
data->value = value;
1447+
data->key = key;
1448+
14181449
swift_retain((void *)value);
14191450
}
1420-
FlsSetValue(key, value);
1451+
1452+
FlsSetValue(key, data);
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)