Skip to content

Commit 14d39c0

Browse files
committed
[silgenpattern] Fix ownership error which could result in a use-after-free.
Specifically, in order to generate runtime errors when we do not handle an enum appropriately, we form a metatype from the input of the switch. The problem is that by the time we get to the leaf of the emission tree where this metatype is created, the input of the switch may have already been consumed. This means creating the metatype could be a use after free. This patch fixes the problem by emitting the value_metatype after we emit the subject of the switch, but before we emit the switch itself. rdar://49562761
1 parent 91dbbc3 commit 14d39c0

File tree

6 files changed

+124
-58
lines changed

6 files changed

+124
-58
lines changed

lib/SILGen/SILGenPattern.cpp

Lines changed: 89 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2497,37 +2497,55 @@ class Lowering::PatternMatchContext {
24972497
PatternMatchEmission &Emission;
24982498
};
24992499

2500+
namespace {
2501+
2502+
struct UnexpectedEnumCaseInfo {
2503+
CanType subjectTy;
2504+
ManagedValue metatype;
2505+
ManagedValue rawValue;
2506+
NullablePtr<const EnumDecl> singleObjCEnum;
2507+
2508+
UnexpectedEnumCaseInfo(CanType subjectTy, ManagedValue metatype,
2509+
ManagedValue rawValue, const EnumDecl *singleObjCEnum)
2510+
: subjectTy(subjectTy), metatype(metatype), rawValue(rawValue),
2511+
singleObjCEnum(singleObjCEnum) {
2512+
assert(isa<MetatypeInst>(metatype));
2513+
assert(bool(rawValue) && isa<UncheckedTrivialBitCastInst>(rawValue));
2514+
assert(singleObjCEnum->hasRawType());
2515+
}
2516+
2517+
UnexpectedEnumCaseInfo(CanType subjectTy, ManagedValue valueMetatype)
2518+
: subjectTy(subjectTy), metatype(valueMetatype), rawValue(),
2519+
singleObjCEnum() {
2520+
assert(isa<ValueMetatypeInst>(valueMetatype));
2521+
}
2522+
2523+
bool isSingleObjCEnum() const { return singleObjCEnum.isNonNull(); }
2524+
2525+
void cleanupInstsIfUnused() {
2526+
auto f = [](SILValue v) {
2527+
if (!v->use_empty())
2528+
return;
2529+
cast<SingleValueInstruction>(v)->eraseFromParent();
2530+
};
2531+
f(metatype.getValue());
2532+
if (rawValue)
2533+
f(rawValue.getValue());
2534+
}
2535+
};
2536+
2537+
} // end anonymous namespace
2538+
25002539
static void emitDiagnoseOfUnexpectedEnumCaseValue(SILGenFunction &SGF,
25012540
SILLocation loc,
2502-
ManagedValue value,
2503-
Type subjectTy,
2504-
const EnumDecl *enumDecl) {
2541+
UnexpectedEnumCaseInfo ueci) {
25052542
ASTContext &ctx = SGF.getASTContext();
25062543
auto diagnoseFailure = ctx.getDiagnoseUnexpectedEnumCaseValue();
25072544
if (!diagnoseFailure) {
25082545
SGF.B.createBuiltinTrap(loc);
25092546
return;
25102547
}
25112548

2512-
assert(enumDecl->isObjC());
2513-
assert(enumDecl->hasRawType());
2514-
assert(value.getType().isTrivial(SGF.F));
2515-
2516-
// Get the enum type as an Any.Type value.
2517-
SILType metatypeType = SGF.getLoweredType(
2518-
AbstractionPattern::getOpaque(),
2519-
MetatypeType::get(subjectTy));
2520-
SILValue metatype = SGF.B.createMetatype(loc, metatypeType);
2521-
2522-
// Bitcast the enum value to its raw type. (This is only safe for @objc
2523-
// enums.)
2524-
SILType loweredRawType = SGF.getLoweredType(enumDecl->getRawType());
2525-
assert(loweredRawType.isTrivial(SGF.F));
2526-
assert(loweredRawType.isObject());
2527-
auto rawValue = SGF.B.createUncheckedTrivialBitCast(loc, value,
2528-
loweredRawType);
2529-
auto materializedRawValue = rawValue.materialize(SGF, loc);
2530-
25312549
auto genericSig = diagnoseFailure->getGenericSignature();
25322550
auto subs = SubstitutionMap::get(
25332551
genericSig,
@@ -2537,49 +2555,40 @@ static void emitDiagnoseOfUnexpectedEnumCaseValue(SILGenFunction &SGF,
25372555
assert(genericParam->getIndex() < 2);
25382556
switch (genericParam->getIndex()) {
25392557
case 0:
2540-
return subjectTy;
2558+
return ueci.subjectTy;
25412559

25422560
case 1:
2543-
return enumDecl->getRawType();
2561+
return ueci.singleObjCEnum.get()->getRawType();
25442562

25452563
default:
25462564
llvm_unreachable("wrong generic signature for expected case value");
25472565
}
25482566
},
25492567
LookUpConformanceInSignature(*genericSig));
25502568

2551-
SGF.emitApplyOfLibraryIntrinsic(loc, diagnoseFailure, subs,
2552-
{ManagedValue::forUnmanaged(metatype),
2553-
materializedRawValue},
2554-
SGFContext());
2569+
SGF.emitApplyOfLibraryIntrinsic(
2570+
loc, diagnoseFailure, subs,
2571+
{ueci.metatype, ueci.rawValue.materialize(SGF, loc)}, SGFContext());
25552572
}
25562573

25572574
static void emitDiagnoseOfUnexpectedEnumCase(SILGenFunction &SGF,
25582575
SILLocation loc,
2559-
ManagedValue value,
2560-
Type subjectTy) {
2576+
UnexpectedEnumCaseInfo ueci) {
25612577
ASTContext &ctx = SGF.getASTContext();
25622578
auto diagnoseFailure = ctx.getDiagnoseUnexpectedEnumCase();
25632579
if (!diagnoseFailure) {
25642580
SGF.B.createBuiltinTrap(loc);
25652581
return;
25662582
}
25672583

2568-
// Get the switched-upon value's type.
2569-
SILType metatypeType = SGF.getLoweredType(
2570-
AbstractionPattern::getOpaque(),
2571-
MetatypeType::get(subjectTy)->getCanonicalType());
2572-
ManagedValue metatype = SGF.B.createValueMetatype(loc, metatypeType, value);
2573-
25742584
auto diagnoseSignature = diagnoseFailure->getGenericSignature();
25752585
auto genericArgsMap = SubstitutionMap::get(
25762586
diagnoseSignature,
2577-
[&](SubstitutableType *type) -> Type { return subjectTy; },
2587+
[&](SubstitutableType *type) -> Type { return ueci.subjectTy; },
25782588
LookUpConformanceInSignature(*diagnoseSignature));
25792589

25802590
SGF.emitApplyOfLibraryIntrinsic(loc, diagnoseFailure, genericArgsMap,
2581-
metatype,
2582-
SGFContext());
2591+
ueci.metatype, SGFContext());
25832592
}
25842593

25852594
static void switchCaseStmtSuccessCallback(SILGenFunction &SGF,
@@ -2778,6 +2787,38 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
27782787
return {subjectMV.copy(*this, S), CastConsumptionKind::TakeAlways};
27792788
}());
27802789

2790+
// If we need to diagnose an unexpected enum case or unexpected enum case
2791+
// value, we need access to a value metatype for the subject. Emit this state
2792+
// now before we emit the actual switch to ensure that the subject has not
2793+
// been consumed.
2794+
auto unexpectedEnumCaseInfo = ([&]() -> UnexpectedEnumCaseInfo {
2795+
SILLocation loc = RegularLocation::getAutoGeneratedLocation();
2796+
CanType canSubjectTy = subjectTy->getCanonicalType();
2797+
CanType metatypeType = MetatypeType::get(canSubjectTy)->getCanonicalType();
2798+
SILType loweredMetatypeType =
2799+
getLoweredType(AbstractionPattern::getOpaque(), metatypeType);
2800+
ManagedValue value = subject.getFinalManagedValue();
2801+
2802+
if (auto *singleEnumDecl = canSubjectTy->getEnumOrBoundGenericEnum()) {
2803+
if (singleEnumDecl->isObjC()) {
2804+
auto metatype = ManagedValue::forUnmanaged(
2805+
B.createMetatype(loc, loweredMetatypeType));
2806+
2807+
// Bitcast the enum value to its raw type. (This is only safe for @objc
2808+
// enums.)
2809+
SILType loweredRawType = getLoweredType(singleEnumDecl->getRawType());
2810+
assert(loweredRawType.isTrivial(F));
2811+
assert(loweredRawType.isObject());
2812+
auto rawValue =
2813+
B.createUncheckedTrivialBitCast(loc, value, loweredRawType);
2814+
return {canSubjectTy, metatype, rawValue, singleEnumDecl};
2815+
}
2816+
}
2817+
2818+
return {canSubjectTy,
2819+
B.createValueMetatype(loc, loweredMetatypeType, value)};
2820+
}());
2821+
27812822
auto failure = [&](SILLocation location) {
27822823
// If we fail to match anything, we trap. This can happen with a switch
27832824
// over an @objc enum, which may contain any value of its underlying type,
@@ -2786,18 +2827,12 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
27862827
SWIFT_DEFER { B.createUnreachable(location); };
27872828

27882829
// Special case: if it's a single @objc enum, we can print the raw value.
2789-
CanType ty = S->getSubjectExpr()->getType()->getCanonicalType();
2790-
if (auto *singleEnumDecl = ty->getEnumOrBoundGenericEnum()) {
2791-
if (singleEnumDecl->isObjC()) {
2792-
emitDiagnoseOfUnexpectedEnumCaseValue(*this, location,
2793-
subject.getFinalManagedValue(),
2794-
subjectTy, singleEnumDecl);
2795-
return;
2796-
}
2830+
if (unexpectedEnumCaseInfo.isSingleObjCEnum()) {
2831+
emitDiagnoseOfUnexpectedEnumCaseValue(*this, location,
2832+
unexpectedEnumCaseInfo);
2833+
return;
27972834
}
2798-
emitDiagnoseOfUnexpectedEnumCase(*this, location,
2799-
subject.getFinalManagedValue(),
2800-
subjectTy);
2835+
emitDiagnoseOfUnexpectedEnumCase(*this, location, unexpectedEnumCaseInfo);
28012836
};
28022837

28032838
// Set up an initial clause matrix.
@@ -2823,6 +2858,10 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
28232858
} else {
28242859
B.emitBlock(contBB);
28252860
}
2861+
2862+
// Now that we have emitted everything, see if our unexpected enum case info
2863+
// metatypes were actually used. If not, delete them.
2864+
unexpectedEnumCaseInfo.cleanupInstsIfUnused();
28262865
}
28272866

28282867
void SILGenFunction::emitSwitchFallthrough(FallthroughStmt *S) {

test/SILGen/enum.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,8 @@ enum SR7799 {
249249
// CHECK: bb0(%0 : $Optional<SR7799>):
250250
// CHECK-NEXT: debug_value %0 : $Optional<SR7799>, let, name "bar", argno 1
251251
// CHECK-NEXT: switch_enum %0 : $Optional<SR7799>, case #Optional.some!enumelt.1: bb1, default bb4
252-
// CHECK: bb1(%3 : $SR7799):
253-
// CHECK-NEXT: switch_enum %3 : $SR7799, case #SR7799.one!enumelt: bb2, case #SR7799.two!enumelt: bb3
252+
// CHECK: bb1([[PHI_ARG:%.*]] : $SR7799):
253+
// CHECK-NEXT: switch_enum [[PHI_ARG]] : $SR7799, case #SR7799.one!enumelt: bb2, case #SR7799.two!enumelt: bb3
254254
func sr7799(bar: SR7799?) {
255255
switch bar {
256256
case .one: print("one")

test/SILGen/enum_resilience.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import resilient_enum
1212
// CHECK-LABEL: sil hidden [ossa] @$s15enum_resilience15resilientSwitchyy0c1_A06MediumOF : $@convention(thin) (@in_guaranteed Medium) -> ()
1313
// CHECK: [[BOX:%.*]] = alloc_stack $Medium
1414
// CHECK-NEXT: copy_addr %0 to [initialization] [[BOX]]
15+
// CHECK-NEXT: [[METATYPE:%.+]] = value_metatype $@thick Medium.Type, [[BOX]] : $*Medium
1516
// CHECK-NEXT: switch_enum_addr [[BOX]] : $*Medium, case #Medium.Paper!enumelt: bb1, case #Medium.Canvas!enumelt: bb2, case #Medium.Pamphlet!enumelt.1: bb3, case #Medium.Postcard!enumelt.1: bb4, default bb5
1617
// CHECK: bb1:
1718
// CHECK-NEXT: dealloc_stack [[BOX]]
@@ -32,7 +33,6 @@ import resilient_enum
3233
// CHECK-NEXT: dealloc_stack [[BOX]]
3334
// CHECK-NEXT: br bb6
3435
// CHECK: bb5:
35-
// CHECK-NEXT: [[METATYPE:%.+]] = value_metatype $@thick Medium.Type, [[BOX]] : $*Medium
3636
// CHECK-NEXT: // function_ref
3737
// CHECK-NEXT: [[DIAGNOSE:%.+]] = function_ref @$ss27_diagnoseUnexpectedEnumCase
3838
// CHECK-NEXT: = apply [[DIAGNOSE]]<Medium>([[METATYPE]]) : $@convention(thin) <τ_0_0> (@thick τ_0_0.Type) -> Never

test/SILGen/enum_resilience_testable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
// CHECK-LABEL: sil hidden [ossa] @$s24enum_resilience_testable15resilientSwitchyy0d1_A06MediumOF : $@convention(thin) (@in_guaranteed Medium) -> ()
1818
// CHECK: [[BOX:%.*]] = alloc_stack $Medium
1919
// CHECK-NEXT: copy_addr %0 to [initialization] [[BOX]]
20+
// CHECK-NEXT: [[METATYPE:%.+]] = value_metatype $@thick Medium.Type, [[BOX]] : $*Medium
2021
// CHECK-NEXT: switch_enum_addr [[BOX]] : $*Medium, case #Medium.Paper!enumelt: bb1, case #Medium.Canvas!enumelt: bb2, case #Medium.Pamphlet!enumelt.1: bb3, case #Medium.Postcard!enumelt.1: bb4, default bb5
2122
// CHECK: bb1:
2223
// CHECK-NEXT: dealloc_stack [[BOX]]
@@ -37,7 +38,6 @@
3738
// CHECK-NEXT: dealloc_stack [[BOX]]
3839
// CHECK-NEXT: br bb6
3940
// CHECK: bb5:
40-
// CHECK-NEXT: [[METATYPE:%.+]] = value_metatype $@thick Medium.Type, [[BOX]] : $*Medium
4141
// CHECK-NEXT: // function_ref
4242
// CHECK-NEXT: [[DIAGNOSE:%.+]] = function_ref @$ss27_diagnoseUnexpectedEnumCase
4343
// CHECK-NEXT: = apply [[DIAGNOSE]]<Medium>([[METATYPE]]) : $@convention(thin) <τ_0_0> (@thick τ_0_0.Type) -> Never

test/SILGen/switch.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,8 +1029,8 @@ func testOptionalEnumMix(_ a : Int?) -> Int {
10291029
case let x?:
10301030
return 0
10311031

1032-
// CHECK: [[SOMEBB]](%3 : $Int):
1033-
// CHECK-NEXT: debug_value %3 : $Int, let, name "x"
1032+
// CHECK: [[SOMEBB]]([[X:%.*]] : $Int):
1033+
// CHECK-NEXT: debug_value [[X]] : $Int, let, name "x"
10341034
// CHECK: integer_literal $Builtin.IntLiteral, 0
10351035

10361036
case .none:
@@ -1051,8 +1051,8 @@ func testOptionalEnumMixWithNil(_ a : Int?) -> Int {
10511051
case let x?:
10521052
return 0
10531053

1054-
// CHECK: [[SOMEBB]](%3 : $Int):
1055-
// CHECK-NEXT: debug_value %3 : $Int, let, name "x"
1054+
// CHECK: [[SOMEBB]]([[X:%.*]] : $Int):
1055+
// CHECK-NEXT: debug_value [[X]] : $Int, let, name "x"
10561056
// CHECK: integer_literal $Builtin.IntLiteral, 0
10571057

10581058
case nil:

test/SILGen/switch_objc.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,30 @@ func matchesEither(input: Hive, a: Hive, b: Hive) -> Bool {
2525
return false
2626
}
2727
}
28+
29+
@objc enum ObjCEnum : UInt8 {
30+
case first
31+
case second
32+
}
33+
34+
// CHECK-LABEL: sil hidden [ossa] @$s11switch_objc44checkObjCEnumUnhandledCaseDiagnosticEmission1xyAA0dE0O_tF : $@convention(thin) (ObjCEnum) -> () {
35+
// CHECK: bb0([[ARG:%.*]] :
36+
// CHECK: [[METATYPE:%.*]] = metatype $@thick ObjCEnum.Type
37+
// CHECK: [[ARG_INT_REPR:%.*]] = unchecked_trivial_bit_cast [[ARG]]
38+
// CHECK: switch_enum [[ARG]] : $ObjCEnum, {{.*}}, default [[DEFAULT_BB:bb[0-9]+]]
39+
//
40+
// CHECK: [[DEFAULT_BB]](
41+
// CHECK: [[STACK_SLOT:%.*]] = alloc_stack $UInt8
42+
// CHECK: store [[ARG_INT_REPR]] to [trivial] [[STACK_SLOT]]
43+
// CHECK: [[DIAGNOSE_FUNC:%.*]] = function_ref @$ss32_diagnoseUnexpectedEnumCaseValue4type03rawE0s5NeverOxm_q_tr0_lF : $@convention(thin) <τ_0_0, τ_0_1> (@thick τ_0_0.Type, @in_guaranteed τ_0_1) -> Never
44+
// CHECK: apply [[DIAGNOSE_FUNC]]<ObjCEnum, UInt8>([[METATYPE]], [[STACK_SLOT]])
45+
// CHECK: unreachable
46+
// CHECK: } // end sil function '$s11switch_objc44checkObjCEnumUnhandledCaseDiagnosticEmission1xyAA0dE0O_tF'
47+
func checkObjCEnumUnhandledCaseDiagnosticEmission(x: ObjCEnum) {
48+
switch x {
49+
case .first:
50+
break
51+
case .second:
52+
break
53+
}
54+
}

0 commit comments

Comments
 (0)