Repeated use of `is` or `as` operators result causes sporadic crashes

Originator:tiarnanmcgrath
Number:rdar://FB9854851 Date Originated:01-22-22
Status:Closed Resolved:
Product:Objective-C Runtime Product Version:
Classification:Application Crash Reproducible:
 
Description
Repeatedly checking if an `AnyClass` type is a certain type using the `is` operand or casting the type using `as?` causes an unexpected crash the Swift core. I've experienced the crash on Swift 5 and up but haven't tested on anything lower.

Reproducing crash
You can reproduce the crash by running the app within the attached xcode project provided.  The issue can also be reproduced in projects with a large number of registered classes. If you enumerate through all registered classes and check if those types are a certain type using `is` then the crash should happen.
```
#0	0x000000018046afdc in ___forwarding___.cold.3 ()
#1	0x00000001803e5368 in ___forwarding___ ()
#2	0x00000001803e738c in _CF_forwarding_prep_0 ()
#3	0x000000018f5b2f98 in swift_dynamicCastObjCClassMetatype ()
#4	0x000000018f572408 in swift_dynamicCastMetatypeImpl(swift::TargetMetadata<swift::InProcess> const*, swift::TargetMetadata<swift::InProcess> const*) ()
```

Other reports of crash:
- https://github.com/Quick/Quick/blob/72e9afa98f00c719b16c359d67d95366bad0a460/Sources/Quick/Configuration/QuickConfiguration.swift#L22-L48

Comments

A reply from Apple

is and as? require that the class being checked subclass NSObject or SwiftObject, or at least implement enough standard ObjC methods to have the same basic functionality. In this case, the casting code is trying to use the isSubclassOfClass: method, and crashing because these dynamic classes don't implement that method.

In general, it's important to be extremely careful with classes obtained from objc_getClassList and objc_copyClassList because they could have arbitrary restrictions on how they're used. The only safe way to use these APIs is to manually check their inheritance by using class_getSuperclass in a loop, or check for protocol conformance with class_conformsToProtocol, and ignore any class that doesn't match.

It's also a good idea to avoid making new root classes. In this particular example, specifying NSObject.self as the superclass in the else case solves the problem, although the code is still dangerous as there could be other classes loaded that don't play well with is TestClass.Type.

One more note: it's strongly recommended to use objc_copyClassList over objc_getClassList. It's difficult to use the get method without potentially racing other threads and ending up with a buffer overflow. The copy version handles this problem for you.

By tiarnanmcgrath at June 2, 2022, 4:33 p.m. (reply...)

Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!