Skip to content

Commit dff98f9

Browse files
authored
Merge pull request #64674 from mikeash/remotemirror-nodefactory-clear-fix
[RemoteMirror] Fix demangle node corruption caused by excessively eager clearing of the NodeFactory.
2 parents dd55791 + fbc3f69 commit dff98f9

File tree

4 files changed

+152
-24
lines changed

4 files changed

+152
-24
lines changed

include/swift/Demangling/Demangler.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ class NodeFactory {
5353
/// The head of the single-linked slab list.
5454
Slab *CurrentSlab = nullptr;
5555

56-
/// The size of the previously allocated slab.
56+
/// The size of the previously allocated slab. This may NOT be the size of
57+
/// CurrentSlab, in the case where a checkpoint has been popped.
5758
///
5859
/// The slab size can only grow, even clear() does not reset the slab size.
5960
/// This initial size is good enough to fit most de-manglings.
@@ -220,6 +221,26 @@ class NodeFactory {
220221
return {copiedString, str.size()};
221222
}
222223

224+
/// A checkpoint which captures the allocator's state at any given time. A
225+
/// checkpoint can be popped to free all allocations made since it was made.
226+
struct Checkpoint {
227+
Slab *Slab;
228+
char *CurPtr;
229+
char *End;
230+
};
231+
232+
/// Create a new checkpoint with the current state of the allocator.
233+
Checkpoint pushCheckpoint() const;
234+
235+
/// Clear all allocations made since the given checkpoint. It is
236+
/// undefined behavior to pop checkpoints in an order other than the
237+
/// order in which they were pushed, or to pop a checkpoint when
238+
/// clear() was called after creating it. The implementation attempts
239+
/// to raise a fatal error in that case, but does not guarantee it. It
240+
/// is allowed to pop outer checkpoints without popping inner ones, or
241+
/// to call clear() without popping existing checkpoints.
242+
void popCheckpoint(Checkpoint checkpoint);
243+
223244
/// Creates a node of kind \p K.
224245
NodePointer createNode(Node::Kind K);
225246

include/swift/RemoteInspection/TypeRefBuilder.h

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,26 @@ class TypeRefBuilder {
456456

457457
Demangle::NodeFactory &getNodeFactory() { return Dem; }
458458

459-
void clearNodeFactory() { Dem.clear(); }
459+
NodeFactory::Checkpoint pushNodeFactoryCheckpoint() const {
460+
return Dem.pushCheckpoint();
461+
}
462+
463+
void popNodeFactoryCheckpoint(NodeFactory::Checkpoint checkpoint) {
464+
Dem.popCheckpoint(checkpoint);
465+
}
466+
467+
class ScopedNodeFactoryCheckpoint {
468+
TypeRefBuilder *Builder;
469+
NodeFactory::Checkpoint Checkpoint;
470+
471+
public:
472+
ScopedNodeFactoryCheckpoint(TypeRefBuilder *Builder)
473+
: Builder(Builder), Checkpoint(Builder->pushNodeFactoryCheckpoint()) {}
474+
475+
~ScopedNodeFactoryCheckpoint() {
476+
Builder->popNodeFactoryCheckpoint(Checkpoint);
477+
}
478+
};
460479

461480
BuiltType decodeMangledType(Node *node, bool forRequirement = true);
462481

@@ -1190,13 +1209,19 @@ class TypeRefBuilder {
11901209
for (auto descriptor : sections.AssociatedType) {
11911210
// Read out the relevant info from the associated type descriptor:
11921211
// The type's name and which protocol conformance it corresponds to
1193-
auto typeRef = readTypeRef(descriptor, descriptor->ConformingTypeName);
1194-
auto typeName = nodeToString(demangleTypeRef(typeRef));
1195-
auto optionalMangledTypeName = normalizeReflectionName(typeRef);
1196-
auto protocolNode = demangleTypeRef(
1197-
readTypeRef(descriptor, descriptor->ProtocolTypeName));
1198-
auto protocolName = nodeToString(protocolNode);
1199-
clearNodeFactory();
1212+
llvm::Optional<std::string> optionalMangledTypeName;
1213+
std::string typeName;
1214+
std::string protocolName;
1215+
{
1216+
ScopedNodeFactoryCheckpoint checkpoint(this);
1217+
auto typeRef =
1218+
readTypeRef(descriptor, descriptor->ConformingTypeName);
1219+
typeName = nodeToString(demangleTypeRef(typeRef));
1220+
optionalMangledTypeName = normalizeReflectionName(typeRef);
1221+
auto protocolNode = demangleTypeRef(
1222+
readTypeRef(descriptor, descriptor->ProtocolTypeName));
1223+
protocolName = nodeToString(protocolNode);
1224+
}
12001225
if (optionalMangledTypeName.has_value()) {
12011226
auto mangledTypeName = optionalMangledTypeName.value();
12021227
if (forMangledTypeName.has_value()) {
@@ -2011,10 +2036,10 @@ class TypeRefBuilder {
20112036
std::unordered_map<std::string, std::string> typeNameToManglingMap;
20122037
for (const auto &section : ReflectionInfos) {
20132038
for (auto descriptor : section.Field) {
2039+
ScopedNodeFactoryCheckpoint checkpoint(this);
20142040
auto TypeRef = readTypeRef(descriptor, descriptor->MangledTypeName);
20152041
auto OptionalMangledTypeName = normalizeReflectionName(TypeRef);
20162042
auto TypeName = nodeToString(demangleTypeRef(TypeRef));
2017-
clearNodeFactory();
20182043
if (OptionalMangledTypeName.has_value()) {
20192044
typeNameToManglingMap[TypeName] = OptionalMangledTypeName.value();
20202045
}

lib/Demangling/Demangler.cpp

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,13 +505,91 @@ void NodeFactory::clear() {
505505
// cycles.
506506
CurrentSlab->Previous = nullptr;
507507
CurPtr = (char *)(CurrentSlab + 1);
508-
assert(End == CurPtr + SlabSize);
509508
#ifdef NODE_FACTORY_DEBUGGING
510509
allocatedMemory = 0;
511510
#endif
512511
}
513512
}
514513

514+
NodeFactory::Checkpoint NodeFactory::pushCheckpoint() const {
515+
return {CurrentSlab, CurPtr, End};
516+
}
517+
518+
void NodeFactory::popCheckpoint(NodeFactory::Checkpoint checkpoint) {
519+
if (checkpoint.Slab == CurrentSlab) {
520+
if (checkpoint.CurPtr > CurPtr) {
521+
fatal(0,
522+
"Popping checkpoint {%p, %p, %p} that is after the current "
523+
"pointer.\n",
524+
checkpoint.Slab, checkpoint.CurPtr, checkpoint.End);
525+
}
526+
if (checkpoint.End != End) {
527+
fatal(0,
528+
"Popping checkpoint {%p, %p, %p} with End that does not match "
529+
"current End %p.\n",
530+
checkpoint.Slab, checkpoint.CurPtr, checkpoint.End, End);
531+
}
532+
#ifndef NDEBUG
533+
// Scribble the newly freed area.
534+
memset(checkpoint.CurPtr, 0xaa, CurPtr - checkpoint.CurPtr);
535+
#endif
536+
CurPtr = checkpoint.CurPtr;
537+
} else {
538+
// We may want to keep using the current slab rather than destroying
539+
// it, since over time this allows us to converge on a steady state
540+
// with no allocation activity (see the comment above in
541+
// NodeFactory::clear). Keep using the current slab if the free space in the
542+
// checkpoint's slab is less than 1/16th of the current slab's space. We
543+
// won't repeatedly allocate and deallocate the current slab. The size
544+
// doubles each time so we'll quickly pass the threshold.
545+
Slab *savedSlab = nullptr;
546+
if (CurrentSlab) {
547+
size_t checkpointSlabFreeSpace = checkpoint.End - checkpoint.CurPtr;
548+
size_t currentSlabSize = End - (char *)(CurrentSlab + 1);
549+
if (currentSlabSize / 16 > checkpointSlabFreeSpace) {
550+
savedSlab = CurrentSlab;
551+
CurrentSlab = CurrentSlab->Previous;
552+
// No need to save End, as it will still be in place later.
553+
}
554+
}
555+
556+
// Free all slabs (possibly except the one we saved) until we find the end
557+
// or we find the checkpoint.
558+
while (CurrentSlab && checkpoint.Slab != CurrentSlab) {
559+
auto Slab = CurrentSlab;
560+
CurrentSlab = CurrentSlab->Previous;
561+
free(Slab);
562+
}
563+
564+
// If we ran to the end and the checkpoint actually has a slab pointer, then
565+
// the checkpoint is invalid.
566+
if (!CurrentSlab && checkpoint.Slab) {
567+
fatal(0,
568+
"Popping checkpoint {%p, %p, %p} with slab that is not within "
569+
"the allocator's slab chain.\n",
570+
checkpoint.Slab, checkpoint.CurPtr, checkpoint.End);
571+
}
572+
573+
if (savedSlab) {
574+
// Reinstall the saved slab.
575+
savedSlab->Previous = CurrentSlab;
576+
CurrentSlab = savedSlab;
577+
CurPtr = (char *)(CurrentSlab + 1);
578+
// End is still valid from before.
579+
} else {
580+
// Set CurPtr and End to match the checkpoint's position.
581+
CurPtr = checkpoint.CurPtr;
582+
End = checkpoint.End;
583+
}
584+
585+
#ifndef NDEBUG
586+
// Scribble the now freed end of the slab.
587+
if (CurPtr)
588+
memset(CurPtr, 0xaa, End - CurPtr);
589+
#endif
590+
}
591+
}
592+
515593
NodePointer NodeFactory::createNode(Node::Kind K) {
516594
return new (Allocate<Node>()) Node(K);
517595
}

stdlib/public/RemoteInspection/TypeRefBuilder.cpp

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ RemoteRef<char> TypeRefBuilder::readTypeRef(uint64_t remoteAddr) {
9393
/// Load and normalize a mangled name so it can be matched with string equality.
9494
llvm::Optional<std::string>
9595
TypeRefBuilder::normalizeReflectionName(RemoteRef<char> reflectionName) {
96+
ScopedNodeFactoryCheckpoint checkpoint(this);
9697
// Remangle the reflection name to resolve symbolic references.
9798
if (auto node = demangleTypeRef(reflectionName,
9899
/*useOpaqueTypeSymbolicReferences*/ false)) {
@@ -104,7 +105,6 @@ TypeRefBuilder::normalizeReflectionName(RemoteRef<char> reflectionName) {
104105
return {};
105106
default:
106107
auto mangling = mangleNode(node);
107-
clearNodeFactory();
108108
if (!mangling.isSuccess()) {
109109
return {};
110110
}
@@ -159,11 +159,11 @@ lookupTypeWitness(const std::string &MangledTypeName,
159159
0)
160160
continue;
161161

162+
ScopedNodeFactoryCheckpoint checkpoint(this);
162163
auto SubstitutedTypeName = readTypeRef(AssocTy,
163164
AssocTy->SubstitutedTypeName);
164165
auto Demangled = demangleTypeRef(SubstitutedTypeName);
165166
auto *TypeWitness = decodeMangledType(Demangled);
166-
clearNodeFactory();
167167

168168
AssociatedTypeCache.insert(std::make_pair(key, TypeWitness));
169169
return TypeWitness;
@@ -181,9 +181,9 @@ const TypeRef *TypeRefBuilder::lookupSuperclass(const TypeRef *TR) {
181181
if (!FD->hasSuperclass())
182182
return nullptr;
183183

184+
ScopedNodeFactoryCheckpoint checkpoint(this);
184185
auto Demangled = demangleTypeRef(readTypeRef(FD, FD->Superclass));
185186
auto Unsubstituted = decodeMangledType(Demangled);
186-
clearNodeFactory();
187187
if (!Unsubstituted)
188188
return nullptr;
189189

@@ -367,9 +367,9 @@ bool TypeRefBuilder::getFieldTypeRefs(
367367
continue;
368368
}
369369

370+
ScopedNodeFactoryCheckpoint checkpoint(this);
370371
auto Demangled = demangleTypeRef(readTypeRef(Field,Field->MangledTypeName));
371372
auto Unsubstituted = decodeMangledType(Demangled);
372-
clearNodeFactory();
373373
if (!Unsubstituted)
374374
return false;
375375

@@ -486,10 +486,10 @@ TypeRefBuilder::getClosureContextInfo(RemoteRef<CaptureDescriptor> CD) {
486486
auto CR = CD.getField(*i);
487487

488488
if (CR->hasMangledTypeName()) {
489+
ScopedNodeFactoryCheckpoint checkpoint(this);
489490
auto MangledName = readTypeRef(CR, CR->MangledTypeName);
490491
auto DemangleTree = demangleTypeRef(MangledName);
491492
TR = decodeMangledType(DemangleTree);
492-
clearNodeFactory();
493493
}
494494
Info.CaptureTypes.push_back(TR);
495495
}
@@ -499,10 +499,10 @@ TypeRefBuilder::getClosureContextInfo(RemoteRef<CaptureDescriptor> CD) {
499499
auto MSR = CD.getField(*i);
500500

501501
if (MSR->hasMangledTypeName()) {
502+
ScopedNodeFactoryCheckpoint checkpoint(this);
502503
auto MangledName = readTypeRef(MSR, MSR->MangledTypeName);
503504
auto DemangleTree = demangleTypeRef(MangledName);
504505
TR = decodeMangledType(DemangleTree);
505-
clearNodeFactory();
506506
}
507507

508508
const MetadataSource *MS = nullptr;
@@ -526,11 +526,11 @@ TypeRefBuilder::getClosureContextInfo(RemoteRef<CaptureDescriptor> CD) {
526526

527527
void TypeRefBuilder::dumpTypeRef(RemoteRef<char> MangledName,
528528
std::ostream &stream, bool printTypeName) {
529+
ScopedNodeFactoryCheckpoint checkpoint(this);
529530
auto DemangleTree = demangleTypeRef(MangledName);
530531
auto TypeName = nodeToString(DemangleTree);
531532
stream << TypeName << "\n";
532533
auto Result = swift::Demangle::decodeMangledType(*this, DemangleTree);
533-
clearNodeFactory();
534534
if (Result.isError()) {
535535
auto *Error = Result.getError();
536536
char *ErrorStr = Error->copyErrorString();
@@ -549,10 +549,14 @@ FieldTypeCollectionResult TypeRefBuilder::collectFieldTypes(
549549
FieldTypeCollectionResult result;
550550
for (const auto &sections : ReflectionInfos) {
551551
for (auto descriptor : sections.Field) {
552-
auto typeRef = readTypeRef(descriptor, descriptor->MangledTypeName);
553-
auto typeName = nodeToString(demangleTypeRef(typeRef));
554-
auto optionalMangledTypeName = normalizeReflectionName(typeRef);
555-
clearNodeFactory();
552+
llvm::Optional<std::string> optionalMangledTypeName;
553+
std::string typeName;
554+
{
555+
ScopedNodeFactoryCheckpoint checkpoint(this);
556+
auto typeRef = readTypeRef(descriptor, descriptor->MangledTypeName);
557+
typeName = nodeToString(demangleTypeRef(typeRef));
558+
optionalMangledTypeName = normalizeReflectionName(typeRef);
559+
}
556560
if (optionalMangledTypeName.has_value()) {
557561
auto mangledTypeName =
558562
optionalMangledTypeName.value();
@@ -618,10 +622,10 @@ void TypeRefBuilder::dumpFieldSection(std::ostream &stream) {
618622
void TypeRefBuilder::dumpBuiltinTypeSection(std::ostream &stream) {
619623
for (const auto &sections : ReflectionInfos) {
620624
for (auto descriptor : sections.Builtin) {
625+
ScopedNodeFactoryCheckpoint checkpoint(this);
621626
auto typeNode =
622627
demangleTypeRef(readTypeRef(descriptor, descriptor->TypeName));
623628
auto typeName = nodeToString(typeNode);
624-
clearNodeFactory();
625629

626630
stream << "\n- " << typeName << ":\n";
627631
stream << "Size: " << descriptor->Size << "\n";
@@ -670,10 +674,10 @@ void TypeRefBuilder::dumpCaptureSection(std::ostream &stream) {
670674
void TypeRefBuilder::dumpMultiPayloadEnumSection(std::ostream &stream) {
671675
for (const auto &sections : ReflectionInfos) {
672676
for (const auto descriptor : sections.MultiPayloadEnum) {
677+
ScopedNodeFactoryCheckpoint checkpoint(this);
673678
auto typeNode =
674679
demangleTypeRef(readTypeRef(descriptor, descriptor->TypeName));
675680
auto typeName = nodeToString(typeNode);
676-
clearNodeFactory();
677681

678682
stream << "\n- " << typeName << ":\n";
679683
stream << " Descriptor Size: " << descriptor->getSizeInBytes() << "\n";

0 commit comments

Comments
 (0)