Skip to content

cDAC: a cyclic Frame chain makes IStackWalk.CreateStackWalk loop forever #135154

Description

@lewing

The cDAC follows Frame.Next without checking that it makes progress. A corrupted Frame chain that contains a cycle makes the stack walk loop forever, even before the first frame is yielded.

Repro

Construct a synthetic thread whose explicit Frame head is a Frame type that doesn't produce a context, with Next pointing to itself. Then call IStackWalk.CreateStackWalk(threadData) and request one frame.

  • On targets without an OS thread context (always on WASM; also dumps without one), GetContextFromFrames (StackWalk_1.cs) skips Frames that don't produce a context and calls FrameIterator.Next() forever. This runs eagerly inside CreateStackWalk, so a caller's frame limit can't bound it.
  • Target reads stop growing (16 in the reported probe) because ProcessedData.GetOrAdd returns the revisited Frame from cache.

Other loops trust Frame.Next in the same way:

  • the SetupContext frame scan;
  • FrameIterator.Next / MoveTo while walking;
  • the frame-address scan near GetContextFromFrames.

The existing guard against no progress only catches a Frame that repeats back-to-back during the walk.

Expected

A cycle in the Frame chain should fail the walk (StackWalkState.Error, matching native SWA_FAILED, or an exception from context derivation) instead of hanging.

A strictly-increasing-address check is not enough. Native Frame::Push (src/coreclr/vm/frames.cpp) allows Frames up to two pages out of order and exempts alternate stacks. Detecting revisited Frame addresses in FrameIterator covers every loop.

Native Frame::Push sets m_Next before publishing the Frame, so a consistent target can't form a cycle. Only corrupted or torn target memory can, which the cDAC must tolerate.

Out of scope

A long but valid chain of Frames that don't produce a context can still cost a lot of reads before the first frame: a padded-descriptor probe read about 10 MB with a one-frame limit. A cycle guard doesn't bound that. Callers need their own read budget.

Note

This issue was drafted with assistance from GitHub Copilot.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions