Skip to content

Commit 10c44e1

Browse files
committed
Always, no, never... forget to check your references
for weak semantics, that is! 94a9c51 made some changes to loading weak references by adding some information in the lower bits with respect to locking. These bits need to be masked out when performing a load, such as when we want to get the metadata pointer for a class instance. This normally works fine when going through the normal weak loading functions in the runtime. When the runtime function swift_ClassMirror_subscript gets the offset of one of its stored properties, it immediately packages it into the the ad-hoc existential container, known as just `Mirror` in the runtime. However, the weak reference isn't aligned! It has bit 1 set. We weren't loading the weak reference here as we would during normal SILGen, such as with a weak_load instruction. Simulate that here and make the reference strong before putting it into the Mirror container, which also clears those lower bits. rdar://problem/27475034 There are still a couple of other cases to handle, namely the unowned(safe) and unowned(unsafe) reference kinds. There may be other places where an unaligned pointer is problematic in the runtime, which we should audit for correctness. rdar://problem/27809991
1 parent 43fb605 commit 10c44e1

File tree

5 files changed

+259
-18
lines changed

5 files changed

+259
-18
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,7 @@ class FieldType {
520520
// some high bits as well.
521521
enum : int_type {
522522
Indirect = 1,
523+
Weak = 2,
523524

524525
TypeMask = ((uintptr_t)-1) & ~(alignof(void*) - 1),
525526
};
@@ -537,10 +538,19 @@ class FieldType {
537538
| (indirect ? Indirect : 0));
538539
}
539540

541+
constexpr FieldType withWeak(bool weak) const {
542+
return FieldType((Data & ~Weak)
543+
| (weak ? Weak : 0));
544+
}
545+
540546
bool isIndirect() const {
541547
return bool(Data & Indirect);
542548
}
543549

550+
bool isWeak() const {
551+
return bool(Data & Weak);
552+
}
553+
544554
const Metadata *getType() const {
545555
return (const Metadata *)(Data & TypeMask);
546556
}

lib/IRGen/GenMeta.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2414,8 +2414,10 @@ namespace {
24142414
NominalTypeDecl::StoredPropertyRange storedProperties){
24152415
SmallVector<FieldTypeInfo, 4> types;
24162416
for (VarDecl *prop : storedProperties) {
2417-
types.push_back(FieldTypeInfo(prop->getType()->getCanonicalType(),
2418-
/*indirect*/ false));
2417+
auto propertyType = prop->getType()->getCanonicalType();
2418+
types.push_back(FieldTypeInfo(propertyType,
2419+
/*indirect*/ false,
2420+
propertyType->is<WeakStorageType>()));
24192421
}
24202422
return getFieldTypeAccessorFn(IGM, type, types);
24212423
}
@@ -2431,7 +2433,7 @@ namespace {
24312433
auto caseType = elt.decl->getArgumentType()->getCanonicalType();
24322434
bool isIndirect = elt.decl->isIndirect()
24332435
|| elt.decl->getParentEnum()->isIndirect();
2434-
types.push_back(FieldTypeInfo(caseType, isIndirect));
2436+
types.push_back(FieldTypeInfo(caseType, isIndirect, /*weak*/ false));
24352437
}
24362438
return getFieldTypeAccessorFn(IGM, type, types);
24372439
}
@@ -2784,9 +2786,13 @@ irgen::emitFieldTypeAccessor(IRGenModule &IGM,
27842786

27852787
auto metadata = IGF.emitTypeMetadataRef(fieldTy);
27862788

2789+
auto fieldTypeInfo = fieldTypes[i];
2790+
27872791
// Mix in flag bits.
2788-
if (fieldTypes[i].isIndirect()) {
2789-
auto flags = FieldType().withIndirect(true);
2792+
if (fieldTypeInfo.hasFlags()) {
2793+
auto flags = FieldType()
2794+
.withIndirect(fieldTypeInfo.isIndirect())
2795+
.withWeak(fieldTypeInfo.isWeak());
27902796
auto metadataBits = IGF.Builder.CreatePtrToInt(metadata, IGF.IGM.SizeTy);
27912797
metadataBits = IGF.Builder.CreateOr(metadataBits,
27922798
llvm::ConstantInt::get(IGF.IGM.SizeTy, flags.getIntValue()));

lib/IRGen/IRGenModule.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,25 +133,30 @@ class IRGenModule;
133133

134134
/// A type descriptor for a field type accessor.
135135
class FieldTypeInfo {
136-
llvm::PointerIntPair<CanType, 1, unsigned> Info;
136+
llvm::PointerIntPair<CanType, 2, unsigned> Info;
137137
/// Bits in the "int" part of the Info pair.
138138
enum : unsigned {
139139
/// Flag indicates that the case is indirectly stored in a box.
140140
Indirect = 1,
141+
/// Indicates a weak optional reference
142+
Weak = 2,
141143
};
142144

143-
static unsigned getFlags(bool indirect) {
144-
return (indirect ? Indirect : 0);
145+
static unsigned getFlags(bool indirect, bool weak) {
146+
return (indirect ? Indirect : 0)
147+
| (weak ? Weak : 0);
145148
// | (blah ? Blah : 0) ...
146149
}
147150

148151
public:
149-
FieldTypeInfo(CanType type, bool indirect)
150-
: Info(type, getFlags(indirect))
152+
FieldTypeInfo(CanType type, bool indirect, bool weak)
153+
: Info(type, getFlags(indirect, weak))
151154
{}
152155

153156
CanType getType() const { return Info.getPointer(); }
154157
bool isIndirect() const { return Info.getInt() & Indirect; }
158+
bool isWeak() const { return Info.getInt() & Weak; }
159+
bool hasFlags() const { return Info.getInt() != 0; }
155160
};
156161

157162
/// The principal singleton which manages all of IR generation.

stdlib/public/runtime/Reflection.mm

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,40 @@ void swift_TupleMirror_subscript(String *outString,
398398
return fieldName;
399399
}
400400

401+
402+
static bool loadSpecialReferenceStorage(HeapObject *owner,
403+
OpaqueValue *fieldData,
404+
const FieldType fieldType,
405+
Mirror *outMirror) {
406+
// TODO: Switch over the other kinds of reference storage here:
407+
// - unowned(safe)
408+
// - unowned(unsafe)
409+
// - class-bound existentials
410+
// - Optional existential types
411+
// This will require a change in the two low flag bits in the field type
412+
// returned from the field type accessor generated by IRGen.
413+
414+
// isWeak() implies a reference type via Sema.
415+
if (!fieldType.isWeak())
416+
return false;
417+
418+
auto weakField = reinterpret_cast<WeakReference *>(fieldData);
419+
auto strongValue = swift_unknownWeakLoadStrong(weakField);
420+
fieldData = reinterpret_cast<OpaqueValue *>(&strongValue);
421+
422+
// This MagicMirror constructor creates a box to hold the loaded refernce
423+
// value, which becomes the new owner for the value.
424+
new (outMirror) MagicMirror(fieldData, fieldType.getType(), /*take*/ true);
425+
426+
// However, swift_StructMirror_subscript and swift_ClassMirror_subscript
427+
// requires that the owner be consumed. Since we have the new heap box as the
428+
// owner now, we need to release the old owner to maintain the contract.
429+
if (owner->metadata->isAnyClass())
430+
swift_unknownRelease(owner);
431+
432+
return true;
433+
}
434+
401435
// -- Struct destructuring.
402436

403437
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
@@ -416,7 +450,7 @@ void swift_StructMirror_subscript(String *outString,
416450
Mirror *outMirror,
417451
intptr_t i,
418452
HeapObject *owner,
419-
const OpaqueValue *value,
453+
OpaqueValue *value,
420454
const Metadata *type) {
421455
auto Struct = static_cast<const StructMetadata *>(type);
422456

@@ -427,13 +461,17 @@ void swift_StructMirror_subscript(String *outString,
427461
auto fieldType = Struct->getFieldTypes()[i];
428462
auto fieldOffset = Struct->getFieldOffsets()[i];
429463

430-
auto bytes = reinterpret_cast<const char*>(value);
431-
auto fieldData = reinterpret_cast<const OpaqueValue *>(bytes + fieldOffset);
464+
auto bytes = reinterpret_cast<char*>(value);
465+
auto fieldData = reinterpret_cast<OpaqueValue *>(bytes + fieldOffset);
432466

433467
new (outString) String(getFieldName(Struct->Description->Struct.FieldNames, i));
434468

435469
// 'owner' is consumed by this call.
436470
assert(!fieldType.isIndirect() && "indirect struct fields not implemented");
471+
472+
if (loadSpecialReferenceStorage(owner, fieldData, fieldType, outMirror))
473+
return;
474+
437475
new (outMirror) Mirror(reflect(owner, fieldData, fieldType.getType()));
438476
}
439477

@@ -614,7 +652,7 @@ void swift_ClassMirror_subscript(String *outString,
614652
Mirror *outMirror,
615653
intptr_t i,
616654
HeapObject *owner,
617-
const OpaqueValue *value,
655+
OpaqueValue *value,
618656
const Metadata *type) {
619657
auto Clas = static_cast<const ClassMetadata*>(type);
620658

@@ -655,10 +693,15 @@ void swift_ClassMirror_subscript(String *outString,
655693
#endif
656694
}
657695

658-
auto bytes = *reinterpret_cast<const char * const*>(value);
659-
auto fieldData = reinterpret_cast<const OpaqueValue *>(bytes + fieldOffset);
660-
661-
new (outString) String(getFieldName(Clas->getDescription()->Class.FieldNames, i));
696+
auto bytes = *reinterpret_cast<char * const *>(value);
697+
auto fieldData = reinterpret_cast<OpaqueValue *>(bytes + fieldOffset);
698+
699+
new (outString) String(getFieldName(Clas->getDescription()->Class.FieldNames,
700+
i));
701+
702+
if (loadSpecialReferenceStorage(owner, fieldData, fieldType, outMirror))
703+
return;
704+
662705
// 'owner' is consumed by this call.
663706
new (outMirror) Mirror(reflect(owner, fieldData, fieldType.getType()));
664707
}

test/1_stdlib/WeakMirror.swift

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
//===--- Mirror.swift -----------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
// RUN: rm -rf %t
13+
// RUN: mkdir -p %t
14+
//
15+
// RUN: if [ %target-runtime == "objc" ]; \
16+
// RUN: then \
17+
// RUN: %target-clang %S/Inputs/Mirror/Mirror.mm -c -o %t/Mirror.mm.o -g && \
18+
// RUN: %target-build-swift -Xfrontend -disable-access-control %s -I %S/Inputs/Mirror/ -Xlinker %t/Mirror.mm.o -o %t/Mirror; \
19+
// RUN: else \
20+
// RUN: %target-build-swift %s -Xfrontend -disable-access-control -o %t/Mirror; \
21+
// RUN: fi
22+
// RUN: %target-run %t/Mirror
23+
// REQUIRES: executable_test
24+
25+
import StdlibUnittest
26+
27+
var mirrors = TestSuite("Mirrors")
28+
29+
class NativeSwiftClass : NativeClassBoundExistential {}
30+
31+
protocol NativeClassBoundExistential : class {}
32+
class NativeSwiftClassHasWeak {
33+
weak var weakProperty: AnyObject?
34+
let x = 0xAABBCCDDEE
35+
}
36+
37+
class NativeSwiftClassHasNativeClassBoundExistential {
38+
weak var weakProperty: NativeClassBoundExistential?
39+
let x = 0xAABBCCDDEE
40+
}
41+
42+
struct StructHasNativeWeakReference {
43+
weak var weakProperty: AnyObject?
44+
let x = 0xAABBCCDDEE
45+
}
46+
47+
mirrors.test("class/NativeSwiftClassHasNativeWeakReference") {
48+
let c1 = NativeSwiftClassHasWeak()
49+
let c2 = NativeSwiftClass()
50+
c1.weakProperty = c2
51+
let classChild = _reflect(c1)[0].1.value
52+
print(classChild)
53+
}
54+
55+
mirrors.test("class/NativeSwiftClassHasNativeClassBoundExistential") {
56+
let c1 = NativeSwiftClassHasNativeClassBoundExistential()
57+
let e = NativeSwiftClass() as NativeClassBoundExistential
58+
c1.weakProperty = e
59+
let classChild = _reflect(c1)[0].1.value
60+
print(classChild)
61+
}
62+
63+
mirrors.test("struct/StructHasNativeWeakReference") {
64+
var s = StructHasNativeWeakReference()
65+
let c2 = NativeSwiftClass()
66+
s.weakProperty = c2
67+
let structChild = _reflect(s)[0].1.value
68+
print(structChild)
69+
}
70+
71+
#if _runtime(_ObjC)
72+
73+
import Foundation
74+
75+
@objc protocol ObjCClassBoundExistential : class {}
76+
77+
@objc protocol ObjCProtocol : class {
78+
weak var weakProperty: AnyObject? { get set }
79+
}
80+
81+
class NativeSwiftClassHasObjCClassBoundExistential {
82+
weak var weakProperty: NSObjectProtocol?
83+
let x = 0xAABBCCDDEE
84+
}
85+
86+
class ObjCClassHasWeak : NSObject {
87+
weak var weakProperty: AnyObject?
88+
let x = 0xAABBCCDDEE
89+
}
90+
91+
class ObjCClassHasNativeClassBoundExistential : NSObject {
92+
weak var weakProperty: NativeClassBoundExistential?
93+
let x = 0xAABBCCDDEE
94+
}
95+
96+
class ObjCClassHasObjCClassBoundExistential : NSObject {
97+
weak var weakProperty: NSObjectProtocol?
98+
let x = 0xAABBCCDDEE
99+
}
100+
101+
struct StructHasObjCWeakReference {
102+
weak var weakProperty: NSObject?
103+
let x = 0xAABBCCDDEE
104+
}
105+
106+
struct StructHasObjCClassBoundExistential {
107+
weak var weakProperty: NSObjectProtocol?
108+
let x = 0xAABBCCDDEE
109+
}
110+
111+
mirrors.test("class/NativeSwiftClassHasObjCWeakReference") {
112+
let c1 = NativeSwiftClassHasWeak()
113+
let nso = NSObject()
114+
c1.weakProperty = nso
115+
let classChild = _reflect(c1)[0].1.value
116+
print(classChild)
117+
}
118+
119+
mirrors.test("class/NativeSwiftClassHasObjCClassBoundExistential") {
120+
let c1 = NativeSwiftClassHasObjCClassBoundExistential()
121+
let nso = NSObject() as NSObjectProtocol
122+
c1.weakProperty = nso
123+
let classChild = _reflect(c1)[0].1.value
124+
print(classChild)
125+
}
126+
127+
mirrors.test("class/ObjCClassHasNativeWeak") {
128+
let c1 = ObjCClassHasWeak()
129+
let c2 = NativeSwiftClass()
130+
c1.weakProperty = c2
131+
let classChild = _reflect(c1)[0].1.value
132+
print(classChild)
133+
}
134+
135+
mirrors.test("class/ObjcCClassHasObjCWeakReference") {
136+
let c1 = ObjCClassHasWeak()
137+
let nso = NSObject()
138+
c1.weakProperty = nso
139+
let classChild = _reflect(c1)[0].1.value
140+
print(classChild)
141+
}
142+
143+
mirrors.test("class/ObjCClassHasNativeClassBoundExistential") {
144+
let c1 = ObjCClassHasNativeClassBoundExistential()
145+
let e = NativeSwiftClass() as NativeClassBoundExistential
146+
c1.weakProperty = e
147+
let classChild = _reflect(c1)[0].1.value
148+
print(classChild)
149+
}
150+
151+
mirrors.test("class/ObjCClassHasObjCClassBoundExistential") {
152+
let c1 = ObjCClassHasObjCClassBoundExistential()
153+
let nsop = NSObject() as NSObjectProtocol
154+
c1.weakProperty = nsop
155+
let classChild = _reflect(c1)[0].1.value
156+
print(classChild)
157+
}
158+
159+
mirrors.test("struct/StructHasObjCWeakReference") {
160+
var s = StructHasObjCWeakReference()
161+
let nso = NSObject()
162+
s.weakProperty = nso
163+
let structChild = _reflect(s)[0].1.value
164+
print(structChild)
165+
}
166+
167+
mirrors.test("struct/StructHasObjCClassBoundExistential") {
168+
var s = StructHasObjCClassBoundExistential()
169+
let nsop = NSObject() as NSObjectProtocol
170+
s.weakProperty = nsop
171+
let structChild = _reflect(s)[0].1.value
172+
print(structChild)
173+
}
174+
175+
#endif
176+
177+
runAllTests()

0 commit comments

Comments
 (0)