Skip to content

Commit 5546bfa

Browse files
authored
Merge pull request #42371 from mikeash/no-forever-loops-5.7
[5.7][RemoteMirror] Put a limit on pointer-chasing loops to avoid infinite loops. Report Slab's Next pointer as a separate chunk.
2 parents 9d26a60 + 14b2838 commit 5546bfa

File tree

2 files changed

+69
-22
lines changed

2 files changed

+69
-22
lines changed

include/swift/Reflection/ReflectionContext.h

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,8 +1377,15 @@ class ReflectionContext
13771377
return std::string("failure reading allocation pool contents");
13781378
auto Pool = reinterpret_cast<const PoolRange *>(PoolBytes.get());
13791379

1380+
// Limit how many iterations of this loop we'll do, to avoid potential
1381+
// infinite loops when reading bad data. Limit to 1 million iterations. In
1382+
// normal operation, each pool allocation is 16kB, so that would be ~16GB of
1383+
// metadata which is far more than any normal program should have.
1384+
unsigned LoopCount = 0;
1385+
unsigned LoopLimit = 1000000;
1386+
13801387
auto TrailerPtr = Pool->Begin + Pool->Remaining;
1381-
while (TrailerPtr) {
1388+
while (TrailerPtr && LoopCount++ < LoopLimit) {
13821389
auto TrailerBytes = getReader()
13831390
.readBytes(RemoteAddress(TrailerPtr), sizeof(PoolTrailer));
13841391
if (!TrailerBytes)
@@ -1426,8 +1433,15 @@ class ReflectionContext
14261433
if (!BacktraceListNextPtr)
14271434
return llvm::None;
14281435

1436+
// Limit how many iterations of this loop we'll do, to avoid potential
1437+
// infinite loops when reading bad data. Limit to 1 billion iterations. In
1438+
// normal operation, a program shouldn't have anywhere near 1 billion
1439+
// metadata allocations.
1440+
unsigned LoopCount = 0;
1441+
unsigned LoopLimit = 1000000000;
1442+
14291443
auto BacktraceListNext = BacktraceListNextPtr->getResolvedAddress();
1430-
while (BacktraceListNext) {
1444+
while (BacktraceListNext && LoopCount++ < LoopLimit) {
14311445
auto HeaderBytes = getReader().readBytes(
14321446
RemoteAddress(BacktraceListNext),
14331447
sizeof(MetadataAllocationBacktraceHeader<Runtime>));
@@ -1473,30 +1487,40 @@ class ReflectionContext
14731487
// provide the whole thing as one big chunk.
14741488
size_t HeaderSize =
14751489
llvm::alignTo(sizeof(*Slab), llvm::Align(MaximumAlignment));
1476-
AsyncTaskAllocationChunk Chunk;
14771490

1478-
Chunk.Start = SlabPtr + HeaderSize;
1479-
Chunk.Length = Slab->CurrentOffset;
1480-
Chunk.Kind = AsyncTaskAllocationChunk::ChunkKind::Unknown;
1491+
AsyncTaskAllocationChunk AllocatedSpaceChunk;
1492+
AllocatedSpaceChunk.Start = SlabPtr + HeaderSize;
1493+
AllocatedSpaceChunk.Length = Slab->CurrentOffset;
1494+
AllocatedSpaceChunk.Kind = AsyncTaskAllocationChunk::ChunkKind::Unknown;
1495+
1496+
// Provide a second chunk just for the Next pointer, so the client knows
1497+
// that there's an allocation there.
1498+
AsyncTaskAllocationChunk NextPtrChunk;
1499+
NextPtrChunk.Start =
1500+
SlabPtr + offsetof(typename StackAllocator::Slab, Next);
1501+
NextPtrChunk.Length = sizeof(Slab->Next);
1502+
NextPtrChunk.Kind = AsyncTaskAllocationChunk::ChunkKind::RawPointer;
14811503

14821504
// Total slab size is the slab's capacity plus the header.
14831505
StoredPointer SlabSize = Slab->Capacity + HeaderSize;
14841506

1485-
return {llvm::None, {Slab->Next, SlabSize, {Chunk}}};
1507+
return {llvm::None,
1508+
{Slab->Next, SlabSize, {NextPtrChunk, AllocatedSpaceChunk}}};
14861509
}
14871510

14881511
std::pair<llvm::Optional<std::string>, AsyncTaskInfo>
1489-
asyncTaskInfo(StoredPointer AsyncTaskPtr) {
1512+
asyncTaskInfo(StoredPointer AsyncTaskPtr, unsigned ChildTaskLimit,
1513+
unsigned AsyncBacktraceLimit) {
14901514
loadTargetPointers();
14911515

14921516
if (supportsPriorityEscalation)
14931517
return asyncTaskInfo<
14941518
AsyncTask<Runtime, ActiveTaskStatusWithEscalation<Runtime>>>(
1495-
AsyncTaskPtr);
1519+
AsyncTaskPtr, ChildTaskLimit, AsyncBacktraceLimit);
14961520
else
14971521
return asyncTaskInfo<
14981522
AsyncTask<Runtime, ActiveTaskStatusWithoutEscalation<Runtime>>>(
1499-
AsyncTaskPtr);
1523+
AsyncTaskPtr, ChildTaskLimit, AsyncBacktraceLimit);
15001524
}
15011525

15021526
std::pair<llvm::Optional<std::string>, ActorInfo>
@@ -1588,7 +1612,8 @@ class ReflectionContext
15881612

15891613
template <typename AsyncTaskType>
15901614
std::pair<llvm::Optional<std::string>, AsyncTaskInfo>
1591-
asyncTaskInfo(StoredPointer AsyncTaskPtr) {
1615+
asyncTaskInfo(StoredPointer AsyncTaskPtr, unsigned ChildTaskLimit,
1616+
unsigned AsyncBacktraceLimit) {
15921617
auto AsyncTaskObj = readObj<AsyncTaskType>(AsyncTaskPtr);
15931618
if (!AsyncTaskObj)
15941619
return {std::string("failure reading async task"), {}};
@@ -1620,8 +1645,9 @@ class ReflectionContext
16201645
Info.RunJob = getRunJob(AsyncTaskObj.get());
16211646

16221647
// Find all child tasks.
1648+
unsigned ChildTaskLoopCount = 0;
16231649
auto RecordPtr = AsyncTaskObj->PrivateStorage.Status.Record;
1624-
while (RecordPtr) {
1650+
while (RecordPtr && ChildTaskLoopCount++ < ChildTaskLimit) {
16251651
auto RecordObj = readObj<TaskStatusRecord<Runtime>>(RecordPtr);
16261652
if (!RecordObj)
16271653
break;
@@ -1661,7 +1687,8 @@ class ReflectionContext
16611687
// Walk the async backtrace.
16621688
if (Info.HasIsRunning && !Info.IsRunning) {
16631689
auto ResumeContext = AsyncTaskObj->ResumeContextAndReserved[0];
1664-
while (ResumeContext) {
1690+
unsigned AsyncBacktraceLoopCount = 0;
1691+
while (ResumeContext && AsyncBacktraceLoopCount++ < AsyncBacktraceLimit) {
16651692
auto ResumeContextObj = readObj<AsyncContext<Runtime>>(ResumeContext);
16661693
if (!ResumeContextObj)
16671694
break;

stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ struct SwiftReflectionContext {
5454
auto Reader = std::make_shared<CMemoryReader>(impl);
5555
nativeContext = new NativeReflectionContext(Reader);
5656
}
57-
57+
5858
~SwiftReflectionContext() {
5959
freeTemporaryAllocation();
6060
delete nativeContext;
@@ -203,9 +203,9 @@ ReflectionSection<Iterator> sectionFromInfo(const swift_reflection_info_t &Info,
203203
auto RemoteSectionStart = (uint64_t)(uintptr_t)Section.section.Begin
204204
- Info.LocalStartAddress
205205
+ Info.RemoteStartAddress;
206-
206+
207207
auto Start = RemoteRef<void>(RemoteSectionStart, Section.section.Begin);
208-
208+
209209
return ReflectionSection<Iterator>(Start,
210210
(uintptr_t)Section.section.End - (uintptr_t)Section.section.Begin);
211211
}
@@ -225,7 +225,7 @@ void
225225
swift_reflection_addReflectionInfo(SwiftReflectionContextRef ContextRef,
226226
swift_reflection_info_t Info) {
227227
auto Context = ContextRef->nativeContext;
228-
228+
229229
// The `offset` fields must be zero.
230230
if (Info.field.offset != 0
231231
|| Info.associated_types.offset != 0
@@ -246,7 +246,7 @@ swift_reflection_addReflectionInfo(SwiftReflectionContextRef ContextRef,
246246
sectionFromInfo<const void *>(Info, Info.reflection_strings),
247247
ReflectionSection<const void *>(nullptr, 0),
248248
ReflectionSection<MultiPayloadEnumDescriptorIterator>(0, 0)};
249-
249+
250250
Context->addReflectionInfo(ContextInfo);
251251
}
252252

@@ -821,10 +821,23 @@ const char *swift_reflection_iterateMetadataAllocationBacktraces(
821821
swift_async_task_slab_return_t
822822
swift_reflection_asyncTaskSlabPointer(SwiftReflectionContextRef ContextRef,
823823
swift_reflection_ptr_t AsyncTaskPtr) {
824-
auto Info = swift_reflection_asyncTaskInfo(ContextRef, AsyncTaskPtr);
824+
auto Context = ContextRef->nativeContext;
825+
826+
// We only care about the AllocatorSlabPtr field. Disable child task and async
827+
// backtrace iteration to save wasted work.
828+
unsigned ChildTaskLimit = 0;
829+
unsigned AsyncBacktraceLimit = 0;
830+
831+
llvm::Optional<std::string> Error;
832+
NativeReflectionContext::AsyncTaskInfo TaskInfo;
833+
std::tie(Error, TaskInfo) =
834+
Context->asyncTaskInfo(AsyncTaskPtr, ChildTaskLimit, AsyncBacktraceLimit);
835+
825836
swift_async_task_slab_return_t Result = {};
826-
Result.Error = Info.Error;
827-
Result.SlabPtr = Info.AllocatorSlabPtr;
837+
if (Error) {
838+
Result.Error = returnableCString(ContextRef, Error);
839+
}
840+
Result.SlabPtr = TaskInfo.AllocatorSlabPtr;
828841
return Result;
829842
}
830843

@@ -866,9 +879,16 @@ swift_async_task_info_t
866879
swift_reflection_asyncTaskInfo(SwiftReflectionContextRef ContextRef,
867880
swift_reflection_ptr_t AsyncTaskPtr) {
868881
auto Context = ContextRef->nativeContext;
882+
883+
// Limit the child task and async backtrace iteration to semi-reasonable
884+
// numbers to avoid doing excessive work on bad data.
885+
unsigned ChildTaskLimit = 1000000;
886+
unsigned AsyncBacktraceLimit = 1000;
887+
869888
llvm::Optional<std::string> Error;
870889
NativeReflectionContext::AsyncTaskInfo TaskInfo;
871-
std::tie(Error, TaskInfo) = Context->asyncTaskInfo(AsyncTaskPtr);
890+
std::tie(Error, TaskInfo) =
891+
Context->asyncTaskInfo(AsyncTaskPtr, ChildTaskLimit, AsyncBacktraceLimit);
872892

873893
swift_async_task_info_t Result = {};
874894
if (Error) {

0 commit comments

Comments
 (0)