Skip to content

read_memory has no watchdog, so a caller cannot promise when it will finish #95

Description

@glslang

The gap

DebugEngine::read_memory reaches the target with no deadline of any kind. Its neighbours have one —
execute_command_bounded and wait_for_event_bounded each arm a watchdog thread holding an
InterruptHandle and Ctrl+Break the engine when the caller's deadline passes — but a typed memory
read goes straight to ReadVirtual and returns when the target gets round to it.

On a dump or a local target that is unobservable. Over KDNET it is not: a read is bytes on a wire, and
windbg-mcp allows up to 1 MiB in one call. A slow-but-healthy link can take tens of seconds to
service that, and an unresponsive target longer still.

Why it matters to a caller

windbg-mcp's transactional batches (glslang/windbg-mcp#96) made this load-bearing. A batch is one
indivisible job on the engine thread, so when a session is torn down mid-transaction the worker tells
the supervisor when the batch will be done and the supervisor waits that out rather than
terminating the worker mid-rollback — on a live kernel, the difference between a patch restored and a
target left patched and halted.

That promise is derived from the batch's budget plus the overrun its executor allows
(batch::OVERRUN_ALLOWANCE), which accounts for watchdog floors — every operation a step can start
is bounded, except this one. A read_memory step is therefore the one place a batch can outrun any
figure the worker advertises.

The exposure today is bounded rather than open: the supervisor allows one more release grace past the
promise before it gives up (release_handoff), so a read has to overrun by more than five seconds
(disconnect) or twenty (end_session) to cost the rollback. A megabyte over a slow link can do that.

What would close it

A bounded variant beside the existing two — read_memory_bounded(addr, size, budget_ms), or a
deadline parameter on the current method — using the same watchdog machinery, so a caller that has
promised a deadline can arm the read to fit inside it.

Worth deciding at the same time whether the watchdog can actually interrupt a ReadVirtual in
progress: SetInterrupt is documented as safe from another thread, but src/dbgeng.rs:726 already
records one case it cannot reach (a live-kernel wait that has not yet connected). If a blocked read
is another, the honest answer may be that the caller's bound is unkeepable and the method should say
so rather than appear to take one.

Dependent change

Once it exists, windbg-mcp's BatchEngine::read_memory should pass the step's remaining budget the
way its command step already passes one to execute_command_bounded, and
batch::OVERRUN_ALLOWANCE's documentation should drop the caveat this issue is about.

Found by automated review on glslang/windbg-mcp#96, after merge.

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions