@@ -3346,6 +3346,91 @@ DictionaryTestSuite.test("BridgedToObjC.Verbatim.FastEnumeration_Empty") {
3346
3346
{ ( $0 as! TestObjCValueTy ) . value } )
3347
3347
}
3348
3348
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
+
3349
3434
//===---
3350
3435
// Dictionary -> NSDictionary bridging tests.
3351
3436
//
@@ -3444,6 +3529,15 @@ DictionaryTestSuite.test("BridgedToObjC.Custom.FastEnumeration_Empty") {
3444
3529
{ ( $0 as! TestObjCValueTy ) . value } )
3445
3530
}
3446
3531
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
+
3447
3541
func getBridgedNSDictionaryOfKey_ValueTypeCustomBridged( ) -> NSDictionary {
3448
3542
assert ( !_isBridgedVerbatimToObjectiveC( TestBridgedKeyTy . self) )
3449
3543
assert ( _isBridgedVerbatimToObjectiveC ( TestObjCValueTy . self) )
@@ -3504,7 +3598,6 @@ DictionaryTestSuite.test("BridgedToObjC.Value_ValueTypeCustomBridged") {
3504
3598
expectAutoreleasedKeysAndValues ( unopt: ( 3 , 3 ) )
3505
3599
}
3506
3600
3507
-
3508
3601
//===---
3509
3602
// NSDictionary -> Dictionary -> NSDictionary bridging tests.
3510
3603
//===---
@@ -4491,7 +4584,7 @@ DictionaryTestSuite.test("dropsBridgedCache") {
4491
4584
}
4492
4585
}
4493
4586
4494
- DictionaryTestSuite . test ( " getObjects:andKeys: " ) {
4587
+ DictionaryTestSuite . test ( " getObjects:andKeys:count: " ) {
4495
4588
let native = [ 1 : " one " , 2 : " two " ] as Dictionary < Int , String >
4496
4589
let d = native as NSDictionary
4497
4590
var keys = UnsafeMutableBufferPointer (
@@ -4512,15 +4605,15 @@ DictionaryTestSuite.test("getObjects:andKeys:") {
4512
4605
expectedValues = [ " two " , " one " ]
4513
4606
}
4514
4607
4515
- d. available_getObjects ( null, andKeys: null) // don't segfault
4608
+ d. available_getObjects ( null, andKeys: null, count : 2 ) // don't segfault
4516
4609
4517
- d. available_getObjects ( null, andKeys: kp)
4610
+ d. available_getObjects ( null, andKeys: kp, count : 2 )
4518
4611
expectEqual ( expectedKeys, Array ( keys) )
4519
4612
4520
- d. available_getObjects ( vp, andKeys: null)
4613
+ d. available_getObjects ( vp, andKeys: null, count : 2 )
4521
4614
expectEqual ( expectedValues, Array ( values) )
4522
4615
4523
- d. available_getObjects ( vp, andKeys: kp)
4616
+ d. available_getObjects ( vp, andKeys: kp, count : 2 )
4524
4617
expectEqual ( expectedKeys, Array ( keys) )
4525
4618
expectEqual ( expectedValues, Array ( values) )
4526
4619
}
0 commit comments