Skip to content

Commit ba9946a

Browse files
committed
Add a dumper/verifier for OpaqueExistentialContainers (i.e. Any) in c++.
The dumper method dumps: 1. The container's metadata pointer. 2. A pointer to the container's value. 3. Whether or not said value is stored inline in the container. This provides a general overview that can be used even when working with SIL code in the debugger by grabbing a pointer to swift Anys and then calling the c++ any method upon them. The verifier is intended to be used in conjunction with ASAN for maximum effect to catch use-after-frees of existential boxes. While implementing this I refactored some code from ExistentialTypeMetadata into methods on OpaqueExistentialContainer. ExistentialTypeMetadata just calls these methods now instead of implementing the code inline.
1 parent be1a2c3 commit ba9946a

File tree

8 files changed

+203
-80
lines changed

8 files changed

+203
-80
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//===--- ExistentialContainer.h -------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_RUNTIME_EXISTENTIALCONTAINER_H
14+
#define SWIFT_RUNTIME_EXISTENTIALCONTAINER_H
15+
16+
#include "swift/Runtime/Metadata.h"
17+
18+
namespace swift {
19+
20+
/// The basic layout of an opaque (non-class-bounded) existential type.
21+
template <typename Runtime>
22+
struct TargetOpaqueExistentialContainer {
23+
ValueBuffer Buffer;
24+
const TargetMetadata<Runtime> *Type;
25+
// const void *WitnessTables[];
26+
27+
const TargetWitnessTable<Runtime> **getWitnessTables() {
28+
return reinterpret_cast<const TargetWitnessTable<Runtime> **>(this + 1);
29+
}
30+
31+
const TargetWitnessTable<Runtime> *const *getWitnessTables() const {
32+
return reinterpret_cast<const TargetWitnessTable<Runtime> *const *>(this +
33+
1);
34+
}
35+
36+
void copyTypeInto(swift::TargetOpaqueExistentialContainer<Runtime> *dest,
37+
unsigned numTables) const {
38+
dest->Type = Type;
39+
for (unsigned i = 0; i != numTables; ++i)
40+
dest->getWitnessTables()[i] = getWitnessTables()[i];
41+
}
42+
43+
/// Return true if this opaque existential container contains a value that is
44+
/// stored inline in the container. Returns false if the value is stored out
45+
/// of line.
46+
bool isValueInline() const;
47+
48+
/// Project out a pointer to the value stored in the container.
49+
///
50+
/// *NOTE* If the container contains the value inline, then this will return a
51+
/// pointer inside the container itself. Otherwise, it will return a pointer
52+
/// to out of line memory.
53+
const OpaqueValue *projectValue() const;
54+
55+
/// Cleans up an existential container instance whose value is uninitialized.
56+
void deinit();
57+
58+
#ifndef NDEBUG
59+
/// Verify invariants of the container.
60+
///
61+
/// We verify that:
62+
///
63+
/// 1. The container itself is in live memory.
64+
/// 2. If we have an out of line value, that the value is in live memory.
65+
///
66+
/// The intention is that this is used in combination with ASAN or Guard
67+
/// Malloc to catch use-after-frees.
68+
void verify() const LLVM_ATTRIBUTE_USED;
69+
70+
/// Dump information about this specific box and its contents. Only intended
71+
/// for use in the debugger.
72+
LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED,
73+
"Only meant for use in the debugger");
74+
#endif
75+
};
76+
using OpaqueExistentialContainer = TargetOpaqueExistentialContainer<InProcess>;
77+
78+
/// The basic layout of a class-bounded existential type.
79+
template <typename ContainedValue>
80+
struct ClassExistentialContainerImpl {
81+
ContainedValue Value;
82+
83+
const WitnessTable **getWitnessTables() {
84+
return reinterpret_cast<const WitnessTable **>(this + 1);
85+
}
86+
const WitnessTable *const *getWitnessTables() const {
87+
return reinterpret_cast<const WitnessTable *const *>(this + 1);
88+
}
89+
90+
void copyTypeInto(ClassExistentialContainerImpl *dest,
91+
unsigned numTables) const {
92+
for (unsigned i = 0; i != numTables; ++i)
93+
dest->getWitnessTables()[i] = getWitnessTables()[i];
94+
}
95+
};
96+
using ClassExistentialContainer = ClassExistentialContainerImpl<void *>;
97+
using WeakClassExistentialContainer =
98+
ClassExistentialContainerImpl<WeakReference>;
99+
100+
} // end swift namespace
101+
102+
#endif

include/swift/Runtime/Metadata.h

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,54 +1895,6 @@ struct TargetWitnessTable {
18951895

18961896
using WitnessTable = TargetWitnessTable<InProcess>;
18971897

1898-
/// The basic layout of an opaque (non-class-bounded) existential type.
1899-
template <typename Runtime>
1900-
struct TargetOpaqueExistentialContainer {
1901-
ValueBuffer Buffer;
1902-
const TargetMetadata<Runtime> *Type;
1903-
// const void *WitnessTables[];
1904-
1905-
const TargetWitnessTable<Runtime> **getWitnessTables() {
1906-
return reinterpret_cast<const TargetWitnessTable<Runtime> **>(this + 1);
1907-
}
1908-
1909-
const TargetWitnessTable<Runtime> * const *getWitnessTables() const {
1910-
return reinterpret_cast<const TargetWitnessTable<Runtime> * const *>(
1911-
this + 1);
1912-
}
1913-
1914-
void copyTypeInto(swift::TargetOpaqueExistentialContainer<Runtime> *dest,
1915-
unsigned numTables) const {
1916-
dest->Type = Type;
1917-
for (unsigned i = 0; i != numTables; ++i)
1918-
dest->getWitnessTables()[i] = getWitnessTables()[i];
1919-
}
1920-
};
1921-
using OpaqueExistentialContainer
1922-
= TargetOpaqueExistentialContainer<InProcess>;
1923-
1924-
/// The basic layout of a class-bounded existential type.
1925-
template <typename ContainedValue>
1926-
struct ClassExistentialContainerImpl {
1927-
ContainedValue Value;
1928-
1929-
const WitnessTable **getWitnessTables() {
1930-
return reinterpret_cast<const WitnessTable**>(this + 1);
1931-
}
1932-
const WitnessTable * const *getWitnessTables() const {
1933-
return reinterpret_cast<const WitnessTable* const *>(this + 1);
1934-
}
1935-
1936-
void copyTypeInto(ClassExistentialContainerImpl *dest,
1937-
unsigned numTables) const {
1938-
for (unsigned i = 0; i != numTables; ++i)
1939-
dest->getWitnessTables()[i] = getWitnessTables()[i];
1940-
}
1941-
};
1942-
using ClassExistentialContainer = ClassExistentialContainerImpl<void *>;
1943-
using WeakClassExistentialContainer =
1944-
ClassExistentialContainerImpl<WeakReference>;
1945-
19461898
/// The possible physical representations of existential types.
19471899
enum class ExistentialTypeRepresentation {
19481900
/// The type uses an opaque existential representation.
@@ -1985,6 +1937,7 @@ struct TargetExistentialTypeMetadata : public TargetMetadata<Runtime> {
19851937
OpaqueValue *projectValue(OpaqueValue *container) const {
19861938
return const_cast<OpaqueValue *>(projectValue((const OpaqueValue*)container));
19871939
}
1940+
19881941
/// Get the dynamic type from an existential container of the type described
19891942
/// by this metadata.
19901943
const TargetMetadata<Runtime> *

include/swift/Runtime/Reflection.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
//
1515
//===----------------------------------------------------------------------===//
1616

17+
#include "swift/Runtime/ExistentialContainer.h"
1718
#include "swift/Runtime/Metadata.h"
1819
#include <cstdlib>
1920

stdlib/public/runtime/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ set(swift_runtime_sources
4848
Errors.cpp
4949
ErrorDefaultImpls.cpp
5050
Exclusivity.cpp
51+
ExistentialContainer.cpp
5152
Heap.cpp
5253
HeapObject.cpp
5354
ImageInspectionMachO.cpp

stdlib/public/runtime/Casting.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,27 @@
1414
//
1515
//===----------------------------------------------------------------------===//
1616

17+
#include "swift/Runtime/Casting.h"
18+
#include "../SwiftShims/RuntimeShims.h"
19+
#include "ErrorObject.h"
20+
#include "ExistentialMetadataImpl.h"
21+
#include "Private.h"
22+
#include "SwiftHashableSupport.h"
23+
#include "stddef.h"
1724
#include "swift/Basic/LLVM.h"
1825
#include "swift/Basic/Lazy.h"
1926
#include "swift/Demangling/Demangler.h"
20-
#include "swift/Runtime/Casting.h"
2127
#include "swift/Runtime/Config.h"
28+
#include "swift/Runtime/Debug.h"
2229
#include "swift/Runtime/Enum.h"
30+
#include "swift/Runtime/ExistentialContainer.h"
2331
#include "swift/Runtime/HeapObject.h"
2432
#include "swift/Runtime/Metadata.h"
2533
#include "swift/Runtime/Mutex.h"
2634
#include "swift/Runtime/Unreachable.h"
2735
#include "llvm/ADT/DenseMap.h"
2836
#include "llvm/ADT/PointerIntPair.h"
2937
#include "llvm/Support/Compiler.h"
30-
#include "swift/Runtime/Debug.h"
31-
#include "ErrorObject.h"
32-
#include "ExistentialMetadataImpl.h"
33-
#include "Private.h"
34-
#include "SwiftHashableSupport.h"
35-
#include "../SwiftShims/RuntimeShims.h"
36-
#include "stddef.h"
3738
#if SWIFT_OBJC_INTEROP
3839
#include "swift/Runtime/ObjCBridge.h"
3940
#include "SwiftValue.h"
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//===--- ExistentialContainer.cpp -----------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "swift/Runtime/ExistentialContainer.h"
14+
#include "swift/Runtime/HeapObject.h"
15+
16+
using namespace swift;
17+
18+
//===----------------------------------------------------------------------===//
19+
// OpaqueExistentialContainer Implementation
20+
//===----------------------------------------------------------------------===//
21+
22+
template <>
23+
bool OpaqueExistentialContainer::isValueInline() const {
24+
return Type->getValueWitnesses()->isValueInline();
25+
}
26+
27+
template <>
28+
const OpaqueValue *OpaqueExistentialContainer::projectValue() const {
29+
auto *vwt = Type->getValueWitnesses();
30+
31+
if (vwt->isValueInline())
32+
return reinterpret_cast<const OpaqueValue *>(&Buffer);
33+
34+
// Compute the byte offset of the object in the box.
35+
unsigned alignMask = vwt->getAlignmentMask();
36+
unsigned byteOffset = (sizeof(HeapObject) + alignMask) & ~alignMask;
37+
auto *bytePtr = reinterpret_cast<const char *>(
38+
*reinterpret_cast<HeapObject *const *const>(&Buffer));
39+
return reinterpret_cast<const OpaqueValue *>(bytePtr + byteOffset);
40+
}
41+
42+
template <>
43+
void OpaqueExistentialContainer::deinit() {
44+
auto *vwt = Type->getValueWitnesses();
45+
if (vwt->isValueInline()) {
46+
return;
47+
}
48+
49+
unsigned alignMask = vwt->getAlignmentMask();
50+
unsigned size = vwt->size;
51+
swift_deallocObject(*reinterpret_cast<HeapObject **>(&Buffer), size,
52+
alignMask);
53+
}
54+
55+
#ifndef NDEBUG
56+
57+
// *NOTE* This routine performs unused memory reads on purpose to try to catch
58+
// use-after-frees in conjunction with ASAN or Guard Malloc.
59+
template <>
60+
void OpaqueExistentialContainer::verify() const {
61+
// We do not actually care about value. We just want to see if the
62+
// memory is valid or not. So convert to a uint8_t and try to
63+
// memcpy into firstByte. We use volatile to just ensure that this
64+
// does not get dead code eliminated.
65+
uint8_t firstByte;
66+
memcpy(&firstByte, projectValue(), 1);
67+
volatile uint8_t firstVolatileByte = firstByte;
68+
(void)firstVolatileByte;
69+
}
70+
71+
/// Dump information about this specific container and its contents.
72+
template <>
73+
void OpaqueExistentialContainer::dump() const {
74+
// Quickly verify to make sure we are well formed.
75+
verify();
76+
77+
printf("TargetOpaqueExistentialContainer.\n");
78+
printf("Metadata Pointer: %p.\n", Type);
79+
printf("Value Pointer: %p.\n", projectValue());
80+
printf("Is Value Stored Inline: %s.\n", isValueInline() ? "true" : "false");
81+
}
82+
83+
#endif

stdlib/public/runtime/ExistentialMetadataImpl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#define SWIFT_RUNTIME_EXISTENTIALMETADATAIMPL_H
2020

2121
#include "MetadataImpl.h"
22+
#include "swift/Runtime/ExistentialContainer.h"
2223

2324
namespace swift {
2425
namespace metadataimpl {

stdlib/public/runtime/Metadata.cpp

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/Basic/Range.h"
2222
#include "swift/Demangling/Demangler.h"
2323
#include "swift/Runtime/Casting.h"
24+
#include "swift/Runtime/ExistentialContainer.h"
2425
#include "swift/Runtime/HeapObject.h"
2526
#include "swift/Runtime/Mutex.h"
2627
#include "swift/Strings.h"
@@ -2240,15 +2241,12 @@ ExistentialTypeMetadata::mayTakeValue(const OpaqueValue *container) const {
22402241
case ExistentialTypeRepresentation::Class:
22412242
return true;
22422243
// Opaque existential containers uniquely own their contained value.
2243-
case ExistentialTypeRepresentation::Opaque:
2244-
{
2244+
case ExistentialTypeRepresentation::Opaque: {
22452245
// We can't take from a shared existential box without checking uniqueness.
22462246
auto *opaque =
22472247
reinterpret_cast<const OpaqueExistentialContainer *>(container);
2248-
auto *vwt = opaque->Type->getValueWitnesses();
2249-
return vwt->isValueInline();
2248+
return opaque->isValueInline();
22502249
}
2251-
22522250
// References to boxed existential containers may be shared.
22532251
case ExistentialTypeRepresentation::Error: {
22542252
// We can only take the value if the box is a bridged NSError, in which case
@@ -2275,13 +2273,7 @@ const {
22752273

22762274
case ExistentialTypeRepresentation::Opaque: {
22772275
auto *opaque = reinterpret_cast<OpaqueExistentialContainer *>(container);
2278-
auto *vwt = opaque->Type->getValueWitnesses();
2279-
if (!vwt->isValueInline()) {
2280-
unsigned alignMask = vwt->getAlignmentMask();
2281-
unsigned size = vwt->size;
2282-
swift_deallocObject(*reinterpret_cast<HeapObject **>(&opaque->Buffer),
2283-
size, alignMask);
2284-
}
2276+
opaque->deinit();
22852277
break;
22862278
}
22872279

@@ -2303,18 +2295,7 @@ ExistentialTypeMetadata::projectValue(const OpaqueValue *container) const {
23032295
case ExistentialTypeRepresentation::Opaque: {
23042296
auto *opaqueContainer =
23052297
reinterpret_cast<const OpaqueExistentialContainer*>(container);
2306-
auto *type = opaqueContainer->Type;
2307-
auto *vwt = type->getValueWitnesses();
2308-
2309-
if (vwt->isValueInline())
2310-
return reinterpret_cast<const OpaqueValue *>(&opaqueContainer->Buffer);
2311-
2312-
unsigned alignMask = vwt->getAlignmentMask();
2313-
// Compute the byte offset of the object in the box.
2314-
unsigned byteOffset = (sizeof(HeapObject) + alignMask) & ~alignMask;
2315-
auto *bytePtr = reinterpret_cast<const char *>(
2316-
*reinterpret_cast<HeapObject *const *const>(&opaqueContainer->Buffer));
2317-
return reinterpret_cast<const OpaqueValue *>(bytePtr + byteOffset);
2298+
return opaqueContainer->projectValue();
23182299
}
23192300
case ExistentialTypeRepresentation::Error: {
23202301
const SwiftError *errorBox

0 commit comments

Comments
 (0)