From 440d2799f7f44eedb9f61b8ec38017f9ed9f8d4e Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Fri, 14 Feb 2020 11:56:35 -0500 Subject: [PATCH] [Stdlib] Eagerly realize EmptyDictionarySingleton and EmptySetSingleton. These objects can escape into ObjC without their class being realized first, which can cause a crash if the unrealized class gets passed into the ObjC runtime. rdar://problem/59295395 --- stdlib/public/core/DictionaryStorage.swift | 1 + stdlib/public/core/SetStorage.swift | 1 + .../EmptyCollectionSingletonRealization.swift | 48 +++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 test/stdlib/EmptyCollectionSingletonRealization.swift diff --git a/stdlib/public/core/DictionaryStorage.swift b/stdlib/public/core/DictionaryStorage.swift index ac931fd2ed51c..bd5c9d583036d 100644 --- a/stdlib/public/core/DictionaryStorage.swift +++ b/stdlib/public/core/DictionaryStorage.swift @@ -117,6 +117,7 @@ internal class __RawDictionaryStorage: __SwiftNativeNSDictionary { // renamed. The old name must not be used in the new runtime. @_fixed_layout @usableFromInline +@_objc_non_lazy_realization internal class __EmptyDictionarySingleton: __RawDictionaryStorage { @nonobjc internal override init(_doNotCallMe: ()) { diff --git a/stdlib/public/core/SetStorage.swift b/stdlib/public/core/SetStorage.swift index 44936c806e1c2..f5c889ce93c1b 100644 --- a/stdlib/public/core/SetStorage.swift +++ b/stdlib/public/core/SetStorage.swift @@ -112,6 +112,7 @@ internal class __RawSetStorage: __SwiftNativeNSSet { // The old names must not be used in the new runtime. @_fixed_layout @usableFromInline +@_objc_non_lazy_realization internal class __EmptySetSingleton: __RawSetStorage { @nonobjc override internal init(_doNotCallMe: ()) { diff --git a/test/stdlib/EmptyCollectionSingletonRealization.swift b/test/stdlib/EmptyCollectionSingletonRealization.swift new file mode 100644 index 0000000000000..2993262622a6c --- /dev/null +++ b/test/stdlib/EmptyCollectionSingletonRealization.swift @@ -0,0 +1,48 @@ +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// RUN: %target-run-simple-swift | %FileCheck %s +// REQUIRES: executable_test +// REQUIRES: objc_interop + +import Foundation + +// Test to make sure that empty collections don't cause a crash if we smuggle +// them into the ObjC runtime without doing anything that would trigger +// realization. The ObjC runtime expects all classes to have been realized +// (i.e. runtime data structures initialized, triggered the first time a class +// is accessed or used) before being queried in any way. +// +// Note: this test deliberately avoids StdlibUnittest to make sure +// no other code runs that might inadvertently trigger realization behind our +// back. + +@objc protocol P {} + +do { + let d: [NSObject: NSObject] = [:] + let c: AnyClass? = object_getClass(d) + let conforms = class_conformsToProtocol(c, P.self) + print("Dictionary: ", conforms) // CHECK: Dictionary: false +} + +do { + let a: [NSObject] = [] + let c: AnyClass? = object_getClass(a) + let p = objc_getProtocol("NSObject") + let conforms = class_conformsToProtocol(c, p) + print("Array:", conforms) // CHECK: Array: false +} + +do { + let s: Set = [] + let c: AnyClass? = object_getClass(s) + let p = objc_getProtocol("NSObject") + let conforms = class_conformsToProtocol(c, p) + print("Set:", conforms) // CHECK: Set: false +} \ No newline at end of file