Skip to content

Commit 91bb2c0

Browse files
committed
[test] check getObjects:andKeys:count: implementations in bridged Dictionaries
1 parent b954e60 commit 91bb2c0

File tree

3 files changed

+112
-15
lines changed

3 files changed

+112
-15
lines changed

stdlib/private/StdlibUnittestFoundationExtras/StdlibUnittestFoundationExtras.swift

+9-6
Original file line numberDiff line numberDiff line change
@@ -100,23 +100,26 @@ extension NSArray {
100100
}
101101
}
102102

103-
@_silgen_name("NSDictionary_getObjects")
104-
func NSDictionary_getObjects(
103+
@_silgen_name("NSDictionary_getObjectsAndKeysWithCount")
104+
func NSDictionary_getObjectsAndKeysWithCount(
105105
nsDictionary: NSDictionary,
106106
objects: AutoreleasingUnsafeMutablePointer<AnyObject?>?,
107-
andKeys keys: AutoreleasingUnsafeMutablePointer<AnyObject?>?
107+
andKeys keys: AutoreleasingUnsafeMutablePointer<AnyObject?>?,
108+
count: Int
108109
)
109110

110111
extension NSDictionary {
111112
@nonobjc // FIXME: there should be no need in this attribute.
112113
public func available_getObjects(
113114
_ objects: AutoreleasingUnsafeMutablePointer<AnyObject?>?,
114-
andKeys keys: AutoreleasingUnsafeMutablePointer<AnyObject?>?
115+
andKeys keys: AutoreleasingUnsafeMutablePointer<AnyObject?>?,
116+
count: Int
115117
) {
116-
return NSDictionary_getObjects(
118+
return NSDictionary_getObjectsAndKeysWithCount(
117119
nsDictionary: self,
118120
objects: objects,
119-
andKeys: keys)
121+
andKeys: keys,
122+
count: count)
120123
}
121124
}
122125

stdlib/private/StdlibUnittestFoundationExtras/UnavailableFoundationMethodThunks.mm

+4-3
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@
2424

2525
SWIFT_CC(swift) SWIFT_RUNTIME_LIBRARY_VISIBILITY
2626
extern "C" void
27-
NSDictionary_getObjects(NSDictionary *_Nonnull nsDictionary,
28-
id *objects, id *keys) {
29-
[nsDictionary getObjects:objects andKeys:keys];
27+
NSDictionary_getObjectsAndKeysWithCount(NSDictionary *_Nonnull nsDictionary,
28+
id *objects, id *keys,
29+
NSInteger count) {
30+
[nsDictionary getObjects:objects andKeys:keys count:count];
3031
SWIFT_CC_PLUSONE_GUARD([nsDictionary release]);
3132
}
3233

validation-test/stdlib/Dictionary.swift

+99-6
Original file line numberDiff line numberDiff line change
@@ -3346,6 +3346,91 @@ DictionaryTestSuite.test("BridgedToObjC.Verbatim.FastEnumeration_Empty") {
33463346
{ ($0 as! TestObjCValueTy).value })
33473347
}
33483348

3349+
/// Check for buffer overruns/underruns in Swift's
3350+
/// `-[NSDictionary getObjects:andKeys:count:]` implementations.
3351+
func checkGetObjectsAndKeys(
3352+
_ dictionary: NSDictionary,
3353+
count: Int,
3354+
file: String = #file,
3355+
line: UInt = #line) {
3356+
let canary = NSObject()
3357+
let storageSize = 2 * max(count, dictionary.count) + 2
3358+
3359+
// Create buffers for storing keys and values at +0 refcounts,
3360+
// then call getObjects:andKeys:count: via a shim in
3361+
// StdlibUnittestFoundationExtras.
3362+
typealias UnmanagedPointer = UnsafeMutablePointer<Unmanaged<AnyObject>>
3363+
var keys = UnmanagedPointer.allocate(capacity: storageSize)
3364+
keys.initialize(to: Unmanaged.passUnretained(canary), count: storageSize)
3365+
var values = UnmanagedPointer.allocate(capacity: storageSize)
3366+
values.initialize(to: Unmanaged.passUnretained(canary), count: storageSize)
3367+
keys.withMemoryRebound(to: AnyObject.self, capacity: storageSize) { k in
3368+
values.withMemoryRebound(to: AnyObject.self, capacity: storageSize) { v in
3369+
dictionary.available_getObjects(
3370+
AutoreleasingUnsafeMutablePointer(v),
3371+
andKeys: AutoreleasingUnsafeMutablePointer(k),
3372+
count: count)
3373+
}
3374+
}
3375+
// Check results.
3376+
for i in 0 ..< storageSize {
3377+
let key = keys[i].takeUnretainedValue()
3378+
let value = values[i].takeUnretainedValue()
3379+
if i < min(count, dictionary.count) {
3380+
expectTrue(
3381+
key !== canary,
3382+
"""
3383+
Buffer underrun at offset \(i) with count \(count):
3384+
keys[\(i)] was left unchanged
3385+
""",
3386+
file: file, line: line)
3387+
expectTrue(
3388+
value !== canary,
3389+
"""
3390+
Buffer underrun at offset \(i) with count \(count):
3391+
values[\(i)] was left unchanged
3392+
""",
3393+
file: file, line: line)
3394+
expectTrue(
3395+
value === dictionary.object(forKey: key) as AnyObject,
3396+
"""
3397+
Inconsistency at offset \(i) with count \(count):
3398+
values[\(i)] does not match value for keys[\(i)]
3399+
""",
3400+
file: file, line: line)
3401+
} else {
3402+
expectTrue(
3403+
key === canary,
3404+
"""
3405+
Buffer overrun at offset \(i) with count \(count):
3406+
keys[\(i)] was overwritten with value \(key)
3407+
""",
3408+
file: file, line: line)
3409+
expectTrue(
3410+
value === canary,
3411+
"""
3412+
Buffer overrun at offset \(i) with count \(count):
3413+
values[\(i)] was overwritten with value \(key)
3414+
""",
3415+
file: file, line: line)
3416+
}
3417+
}
3418+
keys.deinitialize(count: storageSize) // noop
3419+
keys.deallocate()
3420+
values.deinitialize(count: storageSize) // noop
3421+
values.deallocate()
3422+
withExtendedLifetime(canary) {}
3423+
}
3424+
3425+
DictionaryTestSuite.test("BridgedToObjC.Verbatim.getObjects:andKeys:count:") {
3426+
let d = getBridgedNSDictionaryOfRefTypesBridgedVerbatim()
3427+
for count in 0 ..< d.count + 2 {
3428+
checkGetObjectsAndKeys(d, count: count)
3429+
}
3430+
expectCrashLater()
3431+
checkGetObjectsAndKeys(d, count: -1)
3432+
}
3433+
33493434
//===---
33503435
// Dictionary -> NSDictionary bridging tests.
33513436
//
@@ -3444,6 +3529,15 @@ DictionaryTestSuite.test("BridgedToObjC.Custom.FastEnumeration_Empty") {
34443529
{ ($0 as! TestObjCValueTy).value })
34453530
}
34463531

3532+
DictionaryTestSuite.test("BridgedToObjC.Custom.getObjects:andKeys:count:") {
3533+
let d = getBridgedNSDictionaryOfKeyValue_ValueTypesCustomBridged()
3534+
for count in 0 ..< d.count + 2 {
3535+
checkGetObjectsAndKeys(d, count: count)
3536+
}
3537+
expectCrashLater()
3538+
checkGetObjectsAndKeys(d, count: -1)
3539+
}
3540+
34473541
func getBridgedNSDictionaryOfKey_ValueTypeCustomBridged() -> NSDictionary {
34483542
assert(!_isBridgedVerbatimToObjectiveC(TestBridgedKeyTy.self))
34493543
assert(_isBridgedVerbatimToObjectiveC(TestObjCValueTy.self))
@@ -3504,7 +3598,6 @@ DictionaryTestSuite.test("BridgedToObjC.Value_ValueTypeCustomBridged") {
35043598
expectAutoreleasedKeysAndValues(unopt: (3, 3))
35053599
}
35063600

3507-
35083601
//===---
35093602
// NSDictionary -> Dictionary -> NSDictionary bridging tests.
35103603
//===---
@@ -4491,7 +4584,7 @@ DictionaryTestSuite.test("dropsBridgedCache") {
44914584
}
44924585
}
44934586

4494-
DictionaryTestSuite.test("getObjects:andKeys:") {
4587+
DictionaryTestSuite.test("getObjects:andKeys:count:") {
44954588
let native = [1: "one", 2: "two"] as Dictionary<Int, String>
44964589
let d = native as NSDictionary
44974590
var keys = UnsafeMutableBufferPointer(
@@ -4512,15 +4605,15 @@ DictionaryTestSuite.test("getObjects:andKeys:") {
45124605
expectedValues = ["two", "one"]
45134606
}
45144607

4515-
d.available_getObjects(null, andKeys: null) // don't segfault
4608+
d.available_getObjects(null, andKeys: null, count: 2) // don't segfault
45164609

4517-
d.available_getObjects(null, andKeys: kp)
4610+
d.available_getObjects(null, andKeys: kp, count: 2)
45184611
expectEqual(expectedKeys, Array(keys))
45194612

4520-
d.available_getObjects(vp, andKeys: null)
4613+
d.available_getObjects(vp, andKeys: null, count: 2)
45214614
expectEqual(expectedValues, Array(values))
45224615

4523-
d.available_getObjects(vp, andKeys: kp)
4616+
d.available_getObjects(vp, andKeys: kp, count: 2)
45244617
expectEqual(expectedKeys, Array(keys))
45254618
expectEqual(expectedValues, Array(values))
45264619
}

0 commit comments

Comments
 (0)