Skip to content

Commit 91e770b

Browse files
authored
Merge pull request #35851 from tbkka/tbkka/permissiveObjCNullCastHandling54
[5.4] In backwards compatibility mode, be more permissive of Obj-C nulls (#35825)
2 parents 3b9787f + e08413c commit 91e770b

File tree

1 file changed

+109
-21
lines changed

1 file changed

+109
-21
lines changed

stdlib/public/runtime/DynamicCast.cpp

Lines changed: 109 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@ extern "C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM(Sh);
110110
/// Nominal type descriptor for Swift.String.
111111
extern "C" const StructDescriptor NOMINAL_TYPE_DESCR_SYM(SS);
112112

113+
// If this returns `true`, then we will call `fatalError` when we encounter a
114+
// null reference in a storage locaation whose type does not allow null.
115+
static bool unexpectedNullIsFatal() {
116+
return true; // Placeholder for an upcoming check.
117+
}
118+
119+
/// This issues a fatal error or warning if the srcValue contains a null object
120+
/// reference. It is used when the srcType is a non-nullable reference type, in
121+
/// which case it is dangerous to continue with a null reference. The null
122+
/// reference is returned if we're operating in backwards-compatibility mode, so
123+
/// callers still have to check for null.
113124
static HeapObject * getNonNullSrcObject(OpaqueValue *srcValue,
114125
const Metadata *srcType,
115126
const Metadata *destType) {
@@ -120,12 +131,27 @@ static HeapObject * getNonNullSrcObject(OpaqueValue *srcValue,
120131

121132
std::string srcTypeName = nameForMetadata(srcType);
122133
std::string destTypeName = nameForMetadata(destType);
123-
swift::fatalError(/* flags = */ 0,
124-
"Found unexpected null pointer value"
134+
const char *msg = "Found unexpected null pointer value"
125135
" while trying to cast value of type '%s' (%p)"
126-
" to '%s' (%p)\n",
127-
srcTypeName.c_str(), srcType,
128-
destTypeName.c_str(), destType);
136+
" to '%s' (%p)%s\n";
137+
if (unexpectedNullIsFatal()) {
138+
// By default, Swift 5.4 and later issue a fatal error.
139+
swift::fatalError(/* flags = */ 0, msg,
140+
srcTypeName.c_str(), srcType,
141+
destTypeName.c_str(), destType,
142+
"");
143+
} else {
144+
// In backwards compatibility mode, this code will warn and return the null
145+
// reference anyway: If you examine the calls to the function, you'll see
146+
// that most callers fail the cast in that case, but a few casts (e.g., with
147+
// Obj-C or CF destination type) sill succeed in that case. This is
148+
// dangerous, but necessary for compatibility.
149+
swift::warning(/* flags = */ 0, msg,
150+
srcTypeName.c_str(), srcType,
151+
destTypeName.c_str(), destType,
152+
": Continuing with null object, but expect problems later.");
153+
}
154+
return object;
129155
}
130156

131157
/******************************************************************************/
@@ -310,7 +336,9 @@ tryCastFromClassToObjCBridgeable(
310336
_getBridgedObjectiveCType(MetadataState::Complete, destType,
311337
destBridgeWitness).Value;
312338
void *srcObject = getNonNullSrcObject(srcValue, srcType, destType);
313-
if (nullptr == swift_dynamicCastUnknownClass(srcObject, targetBridgedClass)) {
339+
// Note: srcObject can be null here in compatibility mode
340+
if (nullptr == srcObject
341+
|| nullptr == swift_dynamicCastUnknownClass(srcObject, targetBridgedClass)) {
314342
destFailureType = targetBridgedClass;
315343
return DynamicCastResult::Failure;
316344
}
@@ -422,8 +450,12 @@ tryCastToSwiftClass(
422450
switch (srcType->getKind()) {
423451
case MetadataKind::Class: // Swift class => Swift class
424452
case MetadataKind::ObjCClassWrapper: { // Obj-C class => Swift class
425-
void *object = getNonNullSrcObject(srcValue, srcType, destType);
426-
if (auto t = swift_dynamicCastClass(object, destClassType)) {
453+
void *srcObject = getNonNullSrcObject(srcValue, srcType, destType);
454+
// Note: srcObject can be null in compatibility mode.
455+
if (srcObject == nullptr) {
456+
return DynamicCastResult::Failure;
457+
}
458+
if (auto t = swift_dynamicCastClass(srcObject, destClassType)) {
427459
auto castObject = const_cast<void *>(t);
428460
*(reinterpret_cast<void **>(destLocation)) = castObject;
429461
if (takeOnSuccess) {
@@ -465,6 +497,17 @@ tryCastToObjectiveCClass(
465497
case MetadataKind::ObjCClassWrapper: // Obj-C class => Obj-C class
466498
case MetadataKind::ForeignClass: { // CF class => Obj-C class
467499
auto srcObject = getNonNullSrcObject(srcValue, srcType, destType);
500+
// If object is null, then we're in the compatibility mode.
501+
// Earlier cast logic always succeeded `as!` casts of nil
502+
// class references but failed `as?` and `is`
503+
if (srcObject == nullptr) {
504+
if (mayDeferChecks) {
505+
*reinterpret_cast<const void **>(destLocation) = nullptr;
506+
return DynamicCastResult::SuccessViaCopy;
507+
} else {
508+
return DynamicCastResult::Failure;
509+
}
510+
}
468511
auto destObjCClass = destObjCType->Class;
469512
if (auto resultObject
470513
= swift_dynamicCastObjCClass(srcObject, destObjCClass)) {
@@ -504,6 +547,19 @@ tryCastToForeignClass(
504547
case MetadataKind::ObjCClassWrapper: // Obj-C class => CF class
505548
case MetadataKind::ForeignClass: { // CF class => CF class
506549
auto srcObject = getNonNullSrcObject(srcValue, srcType, destType);
550+
// If srcObject is null, then we're in compatibility mode.
551+
// Earlier cast logic always succeeded `as!` casts of nil
552+
// class references. Yes, this is very dangerous, which
553+
// is why we no longer permit it.
554+
if (srcObject == nullptr) {
555+
if (mayDeferChecks) {
556+
*reinterpret_cast<const void **>(destLocation) = nullptr;
557+
return DynamicCastResult::SuccessViaCopy;
558+
} else {
559+
// `as?` and `is` checks always fail on nil sources
560+
return DynamicCastResult::Failure;
561+
}
562+
}
507563
if (auto resultObject
508564
= swift_dynamicCastForeignClass(srcObject, destClassType)) {
509565
*reinterpret_cast<const void **>(destLocation) = resultObject;
@@ -679,6 +735,10 @@ struct ObjCBridgeMemo {
679735
// Use the dynamic ISA type of the object always (Note that this
680736
// also implicitly gives us the ObjC type for a CF object.)
681737
void *srcObject = getNonNullSrcObject(srcValue, srcType, destType);
738+
// If srcObject is null, then we're in backwards compatibility mode.
739+
if (srcObject == nullptr) {
740+
return DynamicCastResult::Failure;
741+
}
682742
Class srcObjCType = object_getClass((id)srcObject);
683743
// Fail if the ObjC object is not a subclass of the bridge class.
684744
while (srcObjCType != targetBridgedObjCClass) {
@@ -1402,15 +1462,26 @@ tryCastToClassExistential(
14021462
case MetadataKind::ObjCClassWrapper:
14031463
case MetadataKind::Class:
14041464
case MetadataKind::ForeignClass: {
1405-
auto object = getNonNullSrcObject(srcValue, srcType, destType);
1465+
auto srcObject = getNonNullSrcObject(srcValue, srcType, destType);
1466+
// If srcObject is null, then we're in compatibility mode.
1467+
// Earlier cast logic always succeeded `as!` casts of nil
1468+
// class references:
1469+
if (srcObject == nullptr) {
1470+
if (mayDeferChecks) {
1471+
*reinterpret_cast<const void **>(destLocation) = nullptr;
1472+
return DynamicCastResult::SuccessViaCopy;
1473+
} else {
1474+
return DynamicCastResult::Failure;
1475+
}
1476+
}
14061477
if (_conformsToProtocols(srcValue, srcType,
14071478
destExistentialType,
14081479
destExistentialLocation->getWitnessTables())) {
1409-
destExistentialLocation->Value = object;
1480+
destExistentialLocation->Value = srcObject;
14101481
if (takeOnSuccess) {
14111482
return DynamicCastResult::SuccessViaTake;
14121483
} else {
1413-
swift_unknownObjectRetain(object);
1484+
swift_unknownObjectRetain(srcObject);
14141485
return DynamicCastResult::SuccessViaCopy;
14151486
}
14161487
}
@@ -1672,8 +1743,14 @@ tryCastToMetatype(
16721743
case MetadataKind::ObjCClassWrapper: {
16731744
#if SWIFT_OBJC_INTEROP
16741745
// Some classes are actually metatypes
1675-
void *object = getNonNullSrcObject(srcValue, srcType, destType);
1676-
if (auto metatype = _getUnknownClassAsMetatype(object)) {
1746+
void *srcObject = getNonNullSrcObject(srcValue, srcType, destType);
1747+
// If object is null, then we're in compatibility mode.
1748+
// Continuing here at all is dangerous, but that's what the
1749+
// pre-Swift-5.4 casting logic did.
1750+
if (srcObject == nullptr) {
1751+
return DynamicCastResult::Failure;
1752+
}
1753+
if (auto metatype = _getUnknownClassAsMetatype(srcObject)) {
16771754
auto srcInnerValue = reinterpret_cast<OpaqueValue *>(&metatype);
16781755
auto srcInnerType = swift_getMetatypeMetadata(metatype);
16791756
return tryCast(destLocation, destType, srcInnerValue, srcInnerType,
@@ -1783,6 +1860,12 @@ tryCastToExistentialMetatype(
17831860
// Some Obj-C classes are actually metatypes
17841861
#if SWIFT_OBJC_INTEROP
17851862
void *srcObject = getNonNullSrcObject(srcValue, srcType, destType);
1863+
// If srcObject is null, we're in compatibility mode.
1864+
// Continuing here at al is dangerous, but that's what the
1865+
// pre-Swift-5.4 casting logic did.
1866+
if (srcObject == nullptr) {
1867+
return DynamicCastResult::Failure;
1868+
}
17861869
if (auto metatype = _getUnknownClassAsMetatype(srcObject)) {
17871870
return _dynamicCastMetatypeToExistentialMetatype(
17881871
destLocation,
@@ -1947,14 +2030,19 @@ tryCast(
19472030
|| srcKind == MetadataKind::ObjCClassWrapper
19482031
|| srcKind == MetadataKind::ForeignClass) {
19492032
auto srcObject = getNonNullSrcObject(srcValue, srcType, destType);
1950-
auto srcDynamicType = swift_getObjectType(srcObject);
1951-
if (srcDynamicType != srcType) {
1952-
srcFailureType = srcDynamicType;
1953-
auto castResult = tryCastToDestType(
1954-
destLocation, destType, srcValue, srcDynamicType,
1955-
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks);
1956-
if (isSuccess(castResult)) {
1957-
return castResult;
2033+
// If srcObject is null, we're in compability mode.
2034+
// But we can't lookup dynamic type for a null class reference, so
2035+
// just skip this in that case.
2036+
if (srcObject != nullptr) {
2037+
auto srcDynamicType = swift_getObjectType(srcObject);
2038+
if (srcDynamicType != srcType) {
2039+
srcFailureType = srcDynamicType;
2040+
auto castResult = tryCastToDestType(
2041+
destLocation, destType, srcValue, srcDynamicType,
2042+
destFailureType, srcFailureType, takeOnSuccess, mayDeferChecks);
2043+
if (isSuccess(castResult)) {
2044+
return castResult;
2045+
}
19582046
}
19592047
}
19602048
}

0 commit comments

Comments
 (0)